Forum

Notifications
Clear all

Custom Operating System

1 Posts
1 Users
0 Reactions
12 Views
 josh
(@josh)
Member Admin
Joined: 2 months ago
Posts: 510
Topic starter  
# Building a Computer Operating System from Scratch

Here's a complete implementation of a minimal operating system with proper file structure and source code:

## File Structure
```
os/
├── Makefile
├── kernel/
│   ├── main.c
│   ├── kernel.h
│   ├── gdt.c
│   ├── gdt.h
│   ├── idt.c
│   ├── idt.h
│   ├── isr.c
│   ├── isr.h
│   ├── timer.c
│   ├── timer.h
│   └── video.c
│   └── video.h
├── boot/
│   ├── boot.s
│   └── loader.asm
├── include/
│   └── stddef.h
└── lib/
    └── string.c
```

## Makefile
```makefile
# Makefile for OS build system

CC = i686-elf-gcc
AS = nasm
CFLAGS = -ffreestanding -fno-exceptions -fno-rtti -fno-stack-protector -nostdlib -Wall -Wextra -std=c11
LDFLAGS = -T linker.ld -ffreestanding -nostdlib

TARGET = os.bin
SOURCES = $(wildcard kernel/*.c) $(wildcard lib/*.c)
OBJECTS = $(SOURCES:.c=.o)

all: $(TARGET)

kernel/%.o: kernel/%.c
	$(CC) $(CFLAGS) -c $< -o $@

lib/%.o: lib/%.c
	$(CC) $(CFLAGS) -c $< -o $@

%.o: %.s
	$(AS) -f elf32 $< -o $@

$(TARGET): boot/boot.s kernel/main.c
	i686-elf-gcc -ffreestanding -nostdlib -T linker.ld boot/boot.s kernel/main.c -o $(TARGET) -Wl,--gc-sections

clean:
	rm -f $(OBJECTS) $(TARGET)

run: $(TARGET)
	qemu-system-i386 -kernel $(TARGET)

.PHONY: all clean run
```

## Bootloader (boot/boot.s)
```assembly
; Bootloader - loads kernel into memory

bits 16
org 0x7c00

start:
    ; Set all segment registers to the same value
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7c00
    
    ; Load kernel from disk
    mov ah, 0x02        ; Read sectors
    mov al, 1           ; Number of sectors to read
    mov ch, 0           ; Cylinder number
    mov cl, 2           ; Sector number (start at sector 2)
    mov dh, 0           ; Head number
    mov dl, 0x80        ; Drive number (first hard drive)
    mov bx, 0x8000      ; Load kernel at 0x8000
    int 0x13            ; Call BIOS disk interrupt
    
    ; Jump to loaded kernel
    jmp 0x8000:0x0000

times 510-($-$$) db 0
dw 0xaa55
```

## Kernel Header (kernel/kernel.h)
```c
#ifndef KERNEL_H
#define KERNEL_H

#include <stddef.h>

// Basic types
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

// System functions
void kernel_main(void);
void kprintf(const char* str);
void kpanic(const char* msg);

#endif // KERNEL_H
```

## Kernel Main (kernel/main.c)
```c
#include "kernel.h"
#include "video.h"
#include "gdt.h"
#include "idt.h"
#include "isr.h"
#include "timer.h"

// Forward declarations of system functions
void kernel_main(void) {
    // Initialize video output
    video_init();
    
    // Initialize Global Descriptor Table
    gdt_init();
    
    // Initialize Interrupt Descriptor Table
    idt_init();
    
    // Initialize Interrupt Service Routines
    isr_init();
    
    // Initialize timer
    timer_init(50); // 50 Hz
    
    // Print welcome message
    kprintf("Welcome to OS from Scratch!\n");
    kprintf("Kernel initialized successfully.\n");
    
    // Infinite loop
    while(1) {
        // Do nothing - system running
    }
}

void kpanic(const char* msg) {
    video_set_color(RED, WHITE);
    kprintf("KERNEL PANIC: ");
    kprintf(msg);
    kprintf("\n");
    while(1);
}
```

## Video Driver (kernel/video.c)
```c
#include "video.h"
#include <stddef.h>

// VGA buffer
static uint16_t* video_buffer = (uint16_t*)0xb8000;
static int cursor_x = 0;
static int cursor_y = 0;

void video_init(void) {
    // Clear screen
    for(int i = 0; i < 80 * 25; i++) {
        video_buffer[i] = (WHITE << 8) | ' ';
    }
    cursor_x = 0;
    cursor_y = 0;
}

void video_put_char(char c, uint8_t color, int x, int y) {
    if(x >= 0 && x < 80 && y >= 0 && y < 25) {
        video_buffer[y * 80 + x] = (color << 8) | c;
    }
}

void video_put_char_at_cursor(char c) {
    if(c == '\n') {
        cursor_x = 0;
        cursor_y++;
    } else if(c == '\r') {
        cursor_x = 0;
    } else {
        video_put_char(c, WHITE, cursor_x, cursor_y);
        cursor_x++;
    }
    
    // Handle scrolling
    if(cursor_x >= 80) {
        cursor_x = 0;
        cursor_y++;
    }
    
    if(cursor_y >= 25) {
        // Scroll up
        for(int i = 0; i < 24 * 80; i++) {
            video_buffer[i] = video_buffer[i + 80];
        }
        for(int i = 24 * 80; i < 25 * 80; i++) {
            video_buffer[i] = (WHITE << 8) | ' ';
        }
        cursor_y = 24;
    }
}

void kprintf(const char* str) {
    while(*str) {
        video_put_char_at_cursor(*str);
        str++;
    }
}
```

## Video Header (kernel/video.h)
```c
#ifndef VIDEO_H
#define VIDEO_H

#include <stdint.h>

// Color definitions
#define BLACK   0x0
#define BLUE    0x1
#define GREEN   0x2
#define CYAN    0x3
#define RED     0x4
#define MAGENTA 0x5
#define BROWN   0x6
#define LIGHT_GRAY 0x7
#define DARK_GRAY 0x8
#define LIGHT_BLUE 0x9
#define LIGHT_GREEN 0xA
#define LIGHT_CYAN 0xB
#define LIGHT_RED 0xC
#define LIGHT_MAGENTA 0xD
#define YELLOW  0xE
#define WHITE   0xF

void video_init(void);
void kprintf(const char* str);

#endif // VIDEO_H
```

## Global Descriptor Table (kernel/gdt.c)
```c
#include "gdt.h"
#include <stdint.h>

// GDT structure
struct gdt_entry {
    uint16_t limit_low;
    uint16_t base_low;
    uint8_t base_middle;
    uint8_t access;
    uint8_t limit_high_flags;
    uint8_t base_high;
} __attribute__((packed));

struct gdt_ptr {
    uint16_t limit;
    uint32_t base;
} __attribute__((packed));

// GDT entries
struct gdt_entry gdt[3];
struct gdt_ptr gp;

// Assembly functions
extern void gdt_flush(uint32_t);

void gdt_init(void) {
    gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
    gp.base = (uint32_t)&gdt;
    
    // Null descriptor
    gdt[0].limit_low = 0;
    gdt[0].base_low = 0;
    gdt[0].base_middle = 0;
    gdt[0].access = 0;
    gdt[0].limit_high_flags = 0;
    gdt[0].base_high = 0;
    
    // Code segment
    gdt[1].limit_low = 0xFFFF;
    gdt[1].base_low = 0;
    gdt[1].base_middle = 0;
    gdt[1].access = 0x9A; // Present, DPL=0, Code, Readable
    gdt[1].limit_high_flags = 0xCF;
    gdt[1].base_high = 0;
    
    // Data segment
    gdt[2].limit_low = 0xFFFF;
    gdt[2].base_low = 0;
    gdt[2].base_middle = 0;
    gdt[2].access = 0x92; // Present, DPL=0, Data, Writable
    gdt[2].limit_high_flags = 0xCF;
    gdt[2].base_high = 0;
    
    gdt_flush((uint32_t)&gp);
}
```

## GDT Header (kernel/gdt.h)
```c
#ifndef GDT_H
#define GDT_H

void gdt_init(void);

#endif // GDT_H
```

## Interrupt Descriptor Table (kernel/idt.c)
```c
#include "idt.h"
#include <stdint.h>

// IDT structure
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));

// IDT table
struct idt_entry idt[256];
struct idt_ptr idtp;

extern void idt_load(uint32_t);

void idt_init(void) {
    idtp.limit = sizeof(struct idt_entry) * 256 - 1;
    idtp.base = (uint32_t)&idt;
    
    // Initialize all entries to zero
    for(int i = 0; i < 256; i++) {
        idt[i].offset_low = 0;
        idt[i].selector = 0x08;
        idt[i].zero = 0;
        idt[i].type_attr = 0x8E;
        idt[i].offset_high = 0;
    }
    
    // Load IDT
    idt_load((uint32_t)&idtp);
}
```

## IDT Header (kernel/idt.h)
```c
#ifndef IDT_H
#define IDT_H

void idt_init(void);

#endif // IDT_H
```

## Interrupt Service Routines (kernel/isr.c)
```c
#include "isr.h"
#include "video.h"
#include <stdint.h>

// ISR handler function pointers
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);

// ISR handler function pointers array
void (*isr_handlers[32])(void) = {
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0
};

// ISR handler function
void isr_handler(struct regs* r) {
    kprintf("Interrupt received: ");
    kprintf("INT ");
    // Simple interrupt number printing would require additional conversion functions
    kprintf("\n");
    
    if(r->int_no < 32) {
        if(isr_handlers[r->int_no] != 0) {
            isr_handlers[r->int_no]();
        }
    }
}

void isr_init(void) {
    // Set up ISRs
    isr_handlers[0] = isr0;
    isr_handlers[1] = isr1;
    isr_handlers[2] = isr2;
    isr_handlers[3] = isr3;
    isr_handlers[4] = isr4;
    isr_handlers[5] = isr5;
    isr_handlers[6] = isr6;
    isr_handlers[7] = isr7;
    isr_handlers[8] = isr8;
    isr_handlers[9] = isr9;
    isr_handlers[10] = isr10;
    isr_handlers[11] = isr11;
    isr_handlers[12] = isr12;
    isr_handlers[13] = isr13;
    isr_handlers[14] = isr14;
    isr_handlers[15] = isr15;
    isr_handlers[16] = isr16;
    isr_handlers[17] = isr17;
    isr_handlers[18] = isr18;
    isr_handlers[19] = isr19;
    isr_handlers[20] = isr20;
    isr_handlers[21] = isr21;
    isr_handlers[22] = isr22;
    isr_handlers[23] = isr23;
    isr_handlers[24] = isr24;
    isr_handlers[25] = isr25;
    isr_handlers[26] = isr26;
    isr_handlers[27] = isr27;
    isr_handlers[28] = isr28;
    isr_handlers[29] = isr29;
    isr_handlers[30] = isr30;
    isr_handlers[31] = isr31;
}
```

## ISR Header (kernel/isr.h)
```c
#ifndef ISR_H
#define ISR_H

struct regs {
    uint32_t gs, fs, es, ds;      // pushed by pusha
    uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;  // pushed by the processor automatically
    uint32_t int_no, err_code;    // pushed by the processor automatically
    uint32_t eip, cs, eflags, user_esp, ss;   // pushed by the processor automatically
};

void isr_init(void);
void isr_handler(struct regs* r);

#endif // ISR_H
```

## Timer Driver (kernel/timer.c)
```c
#include "timer.h"
#include "isr.h"
#include "video.h"
#include <stdint.h>

static uint32_t timer_ticks = 0;

extern void timer_callback(struct regs* r);

void timer_phase(int hz) {
    int divisor = 1193180 / hz;       // Calculate divisor
    outb(0x43, 0x36);             // Set command byte
    outb(0x40, divisor & 0xFF);   // Set low byte of divisor
    outb(0x40, (divisor >> 8) & 0xFF); // Set high byte of divisor
}

void timer_init(int frequency) {
    timer_phase(frequency);
    
    // Register the timer interrupt handler
    isr_handlers[32] = timer_callback;
}

void timer_callback(struct regs* r) {
    timer_ticks++;
    kprintf("Timer tick: ");
    kprintf("TICK ");
    kprintf("\n");
}
```

## Timer Header (kernel/timer.h)
```c
#ifndef TIMER_H
#define TIMER_H

#include <stdint.h>

void timer_init(int frequency);
void timer_phase(int hz);

#endif // TIMER_H
```

## String Library (lib/string.c)
```c
#include <stddef.h>

// Simple string functions for OS
size_t strlen(const char* str) {
    size_t len = 0;
    while(str[len])
        len++;
    return len;
}

char* strcpy(char* dest, const char* src) {
    char* ret = dest;
    while((*dest++ = *src++))
        ;
    return ret;
}

int strcmp(const char* s1, const char* s2) {
    while(*s1 && (*s1 == *s2)) {
        s1++;
        s2++;
    }
    return *(unsigned char*)s1 - *(unsigned char*)s2;
}
```

## Standard Output (lib/stdio.h)
```c
#ifndef STDIO_H
#define STDIO_H

#include <stddef.h>

void outb(unsigned short port, unsigned char val);
unsigned char inb(unsigned short port);

#endif // STDIO_H
```

This is a minimal OS kernel implementation with the following features:
1. Basic memory management (GDT, IDT)
2. Interrupt handling system
3. Timer driver
4. Video output system
5. Simple string manipulation functions

To compile this code you would need:
1. A cross-compiler toolchain (like GCC)
2. Assembly support for interrupt handling
3. Linker script to properly place the kernel
4. Bootloader (like GRUB)

The OS will boot and initialize basic subsystems, then display messages via VGA text mode.
```

   
Quote
Share: