Compare commits

..

29 Commits

Author SHA1 Message Date
9aa1b85ca0 Merge pull request #62 from vmttmv/main
begin fixing build errors for stage2
2025-10-25 17:42:46 -07:00
vmttmv
9216673b18 begin fixing build errors 2025-10-26 03:03:20 +03:00
ed07e2cd9c Delete kernel/linker.ld
Removing linker.ld for the grub legacy stuff I was gonna try
2025-10-25 15:10:55 -07:00
a4318d3c79 Delete kernel/multiboot.h
Remove empty multiboot.h for grub legacy
2025-10-25 15:09:32 -07:00
9cde2e708d Merge pull request #61 from vmttmv/main
stage1: fix load addresses for stage2/kernel
2025-10-25 14:53:23 -07:00
vmttmv
c22f6b6f14 stage1: fix load addresses for stage2/kernel 2025-10-26 00:18:45 +03:00
6267863939 Merge pull request #60 from vmttmv/main
build: debug symbols for stage1
2025-10-25 11:42:52 -07:00
vmttmv
49114214cb build: debug symbols for stage1 2025-10-25 21:26:52 +03:00
e58abdae1c Merge pull request #59 from vmttmv/main
Makefile target organization
2025-10-25 10:28:29 -07:00
vmttmv
dd37ba8ed6 make: explicit build dir, separate stage1 target so it can be called easier 2025-10-25 19:52:39 +03:00
dd68fd805f fixing bootloader again but should work using nasm now 2025-08-06 01:42:17 -07:00
16309bc306 added checksum to boot1 2025-08-03 10:27:25 -07:00
4a189f482f fixed bootloader by adding lba conversion from chs 2025-08-02 21:48:51 -07:00
267130281a fixing the keyboard and bootloader so that its 2 stage again 2025-08-02 20:06:15 -07:00
e1e30b511a mostly improvements to malloc 2025-07-01 11:20:04 -07:00
109e554524 fixing the remaining issues in the kernel directory 2025-06-16 15:13:37 -07:00
69762b6650 adding stub usb and mouse code 2025-05-18 02:49:17 -07:00
49361a98be fixing minor bugs with single unit compilation in gcc with flags on 2025-05-16 01:08:12 -07:00
50efcc13fe minor additions to the kernel heap and adding acpi 2025-05-15 04:22:55 -07:00
a9f2826014 addind more important kernel files and also fixing bugs 2025-05-15 02:37:06 -07:00
512bd49ff7 add the last of the files and some basic stubs for most of the empty files from last commit 2025-05-13 19:17:19 -07:00
799f744f47 doing some memory work and gdt and timer and vga 2025-05-13 11:39:16 -07:00
10b8fdc33f adding and fixing some missing things and some undefined 2025-05-13 10:44:10 -07:00
ecfa54e225 new OS 2025-04-30 23:03:44 -07:00
a464e109cb debugged boot.asm 2025-04-07 02:35:35 -07:00
6dcbfd5683 fix CMakeLists.txt and add build dir to gitignore 2025-04-07 02:20:13 -07:00
df429f0042 applying patch from shoshta73 2025-01-31 12:45:27 -08:00
82e910ca86 adding files as they are from deesix on my IRC via pastebin 2024-09-13 13:18:45 -07:00
f4f71fd3c2 fixing an issue with UNIX style EOL 2024-09-09 15:43:14 -07:00
84 changed files with 3326 additions and 397 deletions

53
.gitignore vendored
View File

@@ -1,52 +1 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
build

7
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"files.associations": {
".fantomasignore": "ignore",
"stddef.h": "c",
"io.h": "c"
}
}

View File

@@ -1,2 +0,0 @@
cmake_minimum_required(VERSION 3.13.4)

View File

@@ -0,0 +1,64 @@
AS = nasm
CC = gcc
CFLAGS = -std=c11 -m32 -ffreestanding -c -fno-stack-protector -fno-pie
LD = ld
QEMU = qemu-system-i386
IMG_SIZE = 1440k
BUILD_DIR = build
BOOT_SRC = bootloader/boot.asm
BOOT_OBJ = $(BUILD_DIR)/boot.o
BOOT_ELF = $(BUILD_DIR)/boot.elf
BOOT_IMG = $(BUILD_DIR)/boot.img
KERNEL_C_SRC = $(wildcard kernel/*.c)
KERNEL_ASM_SRC = $(wildcard kernel/*.asm)
KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC))
KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC))
KERNEL_OBJ += $(BUILD_DIR)/boot1.o
KERNEL_ELF = $(BUILD_DIR)/kernel.elf
KERNEL_BIN = $(BUILD_DIR)/kernel.bin
DISK_IMG = $(BUILD_DIR)/disk.img
all: $(BOOT_IMG) $(KERNEL_BIN) $(DISK_IMG)
$(BUILD_DIR):
mkdir -p $@
$(BOOT_OBJ): $(BOOT_SRC) | $(BUILD_DIR)
$(AS) -f elf32 -g -F dwarf -o $@ $<
$(BOOT_ELF): $(BOOT_OBJ)
$(LD) -Ttext=0x7c00 -melf_i386 -o $@ $<
$(BOOT_IMG): $(BOOT_ELF)
objcopy -O binary $< $@
truncate -s $(IMG_SIZE) $@
$(BUILD_DIR)/boot1.o: bootloader/boot1.asm
$(AS) -f elf32 -o $@ $<
$(BUILD_DIR)/asm_%.o: kernel/%.asm
$(AS) -f elf32 -o $@ $<
$(BUILD_DIR)/%.o: kernel/%.c
$(CC) $(CFLAGS) $< -o $@
$(KERNEL_BIN): $(KERNEL_OBJ) | $(BUILD_DIR)
$(LD) -melf_i386 --oformat binary -T bootloader/linker.ld -o $@ $(KERNEL_OBJ)
$(DISK_IMG): $(BOOT_IMG) $(KERNEL_BIN)
dd if=$(BOOT_IMG) of=$@ bs=512 seek=4
dd if=$(KERNEL_BIN) of=$@ bs=512 seek=200
run: $(DISK_IMG)
$(QEMU) -drive file=$<,format=raw,if=floppy
.PHONY: stage1 clean
stage1: $(BOOT_IMG)
clean:
rm -rf $(BUILD_DIR)

229
README.md
View File

@@ -1,175 +1,54 @@
# ClassicOS
This operating system uses standard operating system concepts used in the 32 bit environment. It will eventually be ported to 64 bit including IA64.
This ClassicOS operating system, aims to support major hardware and software technology existing from when the first 32 bit systems appeared on the market through the early 2000's and most of those have been listed below.
## Programming
This project uses the C library and Assembly language.
## Toolchain
GNU Make 4.2.1
CMake 3.13.4
GNU 8.3.0
gcc (Debian 8.3.0-6) 8.3.0
nasm 2.14
GNU ld (GNU Binutils for Debian) 2.31.1
binutils
For testing, QEMU i386 and TigerVNC/VNCViewer on ::1:5900
## Development (Team, etc)
This project will use MIT or the GPL license and will be fully open source.
Ideal situations aside, My goal has always been:
1-2 person working on bootloader
1-2 person working on kernel
1-2 person working on drivers
1-2 person working on issues/bugs
1-2 people working on applications/user-space
1-2 people working on Networking
1-2 people working on memmory issues, stack issues, etc.
at least one person doing hardware and software testing and writing tests in a test framework
At least one major bug fix a week
At least one minor buf fix a week
At least one new feature a month (or more)
## CPU Processor Support
This project initially aims to support all 32 bit Intel and AMD processors Including;
-- Intel --
i386 SX and DX Processors
i486 SX and DX Processors
Pentium Processors (60 to 120MHz)
Pentium Pro Processors
Pentium II Processors
Pentium II Xeon Processors
Pentium III Processors
Pentium III Xeon Processors
early Pentium 4 Processors (Willamette, Prescott, Northwood)
-- AMD --
AMD k5
AMD K6
AMD am386
AMD am486
Athlon
Duron
Sempron
## Device Support
USB 1.0
USB 1.1
USB 2.0
USB 2.1
SATA 1
UDMA
UltraATA 66/100/133
SCSI-1
SCSI-2
SCSI-3
Ultra-2 SCSI
Ultra-3 SCSI
ESDI
MFM/RLL
## Booting
BIOS from
- primary hard disk partition
- primary floppy
- ISO 9660 CD-ROM, CD-R,CD-RW,
- DVD
- Removable media (Zip, Jaz, USB, Tape, Syquest, Bernoulli, CF, SmartMedia, SD etc)
Might eventually support GRUB/GRUB2 and/or UEFI/EFI.
Include support for AHCI and ACPI
## Bus Support
ISA
EISA
VESA/VESA Local Bus (VLB)
PCI
PCI-X
PCIe 1.0, 1.1, 2.0
AGP
DIN 41416/NuBUS
## Hardware support
This OS aims to support major hardware existing from 1985 to early 2k's.
## Memory Support
up to 4GB
## Features
Has a GUI
Has a IDE
Has a text editor
Has compilers for compiled lanugages (C, C++, C#, Go, Java, Fortran, Pascal, Objective C, Haskell, ADA, Scala, Rust, Zig, Ocaml, Julia, Dart, Erlang, Elixir)
Has a Web Browser
Has a shell, tty, console, terminal
Has interpreters for interpreted languages like python 2 and python 3, JavaScript, BASIC, PHP, etc.
Has a git client
Has a video, audio editing and playing suite
Has a file browser (in GUI)
Has debuggers for languages/compilers, etc that output symbols, etc.
## Video Support
-- Modes --
CGA
EGA
VGA
SVGA
MCGA
XGA
HGA / Hercules
XGA-2
SXGA
UXGA
WXGA
8514/a
VESA SVGA
VESA/VLB
AGP (1.0, 2.0, 3.0, 3.5, Pro) - 66MHz - aka AGP 1X, 2X, 4X, 8X
PCI Graphics
-- Resolutions --
## Networking
Novell NE1000
Novell NE2000
## Drivers
-- Video card(s)
-- NIC's (3Com, Intel, etc.)
-- Audio
## Filesystems
- FAT12
- FAT16/FAT16B/FAT16X
- FAT32/FAT32X
- NTFS
- HPFS
- HFS / HFS+
- ext / ext2 / ext3
- exFAT
- ZFS
- JFS
# ClassicOS
[![Build](https://img.shields.io/badge/build-passing-brightgreen?style=flat-square)](https://github.com/gbowne1/ClassicOS/actions)
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)
[![Platform](https://img.shields.io/badge/platform-x86_IA32-lightgrey?style=flat-square)](https://en.wikipedia.org/wiki/IA-32)
[![Made with](https://img.shields.io/badge/made%20with-C%20%26%20NASM-9cf?style=flat-square)](#)
> **ClassicOS** is a 32-bit Intel x86 operating system built from scratch using C, NASM, and GCC.
> Designed for 386, 486, and Pentium-class CPUs, it runs in protected mode, outputs to VGA text mode and serial ports, and supports floppy/HDD boot with basic FAT support.
---
## ✨ Features
- MBR bootloader at `0x7C00`
- Switch to protected mode with GDT
- A20 gate enabling
- Simple FAT12/FAT16 disk loader stub
- VGA text output (`0xB8000`)
- Serial COM1 support (`0x3F8`)
- Basic kernel (`kmain`) written in C
- Makefile-based build system
- Bootable floppy image for testing in QEMU
---
## ⚙️ Requirements
Youll need the following tools installed:
- `nasm`
- `gcc` (targeting i386)
- `ld`
- `make`
- `qemu-system-i386`
Optional:
- `gdb`
- `vncviewer` (TigerVNC or similar)
---
## 🛠️ Building ClassicOS
Clone and build:
```bash
git clone https://github.com/gbowne1/ClassicOS.git
cd ClassicOS
make
```
build kernel
for %f in (*.c) do gcc -m32 -O0 -Wall -Wextra -Werror -pedantic -ffreestanding -nostdlib -fno-pic -fno-stack-protector -fno-pie -march=i386 -mtune=i386 -c "%f" -o "%f.o"

View File

@@ -1,85 +0,0 @@
section .boot
bits 16
global boot
boot:
mov ax, 0x2401
int 0x15
mov ax, 0x3
int 0x10
mov [disk],dl
mov ah, 0x2 ;read sectors
mov al, 6 ;sectors to read
mov ch, 0 ;cylinder idx
mov dh, 0 ;head idx
mov cl, 2 ;sector idx
mov dl, [disk] ;disk idx
mov bx, copy_target;target pointer
int 0x13
cli
lgdt [gdt_pointer]
mov eax, cr0
or eax,0x1
mov cr0, eax
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp CODE_SEG:boot2
gdt_start:
dq 0x0
gdt_code:
dw 0xFFFF
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_pointer:
dw gdt_end - gdt_start
dd gdt_start
disk:
db 0x0
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
times 510 - ($-$$) db 0
dw 0xaa55
copy_target:
bits 32
hello: db "Hello more than 512 bytes world!!",0
boot2:
mov esi,hello
mov ebx,0xb8000
.loop:
lodsb
or al,al
jz halt
or eax,0x0F00
mov word [ebx], ax
add ebx,2
jmp .loop
halt:
mov esp,kernel_stack_top
extern kmain
call kmain
cli
hlt
section .bss
align 4
kernel_stack_bottom: equ $
resb 16384 ; 16 KB
kernel_stack_top:

0
bootloader/Makefile Normal file
View File

291
bootloader/boot.asm Normal file
View File

@@ -0,0 +1,291 @@
; ==============================================================================
; boot.asm - First Stage Bootloader (CHS Based)
; ==============================================================================
[BITS 16]
; [ORG 0x7C00]
start:
cli ; Disable interrupts
mov [bootdev], dl ; Save boot device number (from BIOS in DL)
; Setup stack safely below EBDA area (choose 0x0000:0x7A00)
xor ax, ax ; AX = 0
mov ss, ax ; Stack segment = 0x0000
mov sp, 0x7A00 ; Stack offset = 0x7A00
; Initialize DS, ES for zero-based segments
xor ax, ax
mov ds, ax
mov es, ax
; Load second-stage bootloader (boot1.asm) to 0x7E00
mov ax, 1 ; LBA of boot1.asm (starts at sector 1)
call lba_to_chs
mov al, 4 ; Number of sectors to read
mov bx, 0x7E00 ; Destination address offset ES = 0 (0x0000:0x7E00)
call read_chs
; Load kernel to 0x100000 (1 MB)
mov ax, 0xffff
mov es, ax ; Set ES for destination address (0xffff << 4 == 0xffff0)
mov ax, 5 ; LBA of kernel start (boot1 is 4 sectors: LBA 14 → kernel at LBA 5)
call lba_to_chs
mov al, 16 ; Number of sectors for kernel
mov bx, 0x10 ; Destination address offset (0xffff:0x10 = 0xffff << 4 + 0x10 = 0x100000)
call read_chs
jc disk_error
; Memory Validation: Verify checksum of second stage bootloader
mov si, 0x7E00 ; Start of second stage
mov cx, 512 * 4 ; Size in bytes (adjust if more sectors loaded)
call verify_checksum
jc disk_error ; Jump if checksum fails
; Enable A20 line
call enable_a20
jc a20_error ; Jump if A20 enable fails
; Setup Global Descriptor Table
call setup_gdt
; Switch to protected mode and jump to second stage at 0x08:0x7E00
call switch_to_pm
disk_error:
mov si, disk_error_msg
call print_string_16
jmp halt
a20_error:
mov si, a20_error_msg
call print_string_16
jmp halt
; ----------------------------------------------------------------
; Verify Checksum Routine
; Uses SI = start address, CX = byte count
; Simple XOR checksum over bytes, expects result 0
verify_checksum:
push ax
push bx
push di
mov di, si
xor al, al
xor bx, bx
.verify_loop:
lodsb
xor bl, al
loop .verify_loop
test bl, bl
jz .checksum_ok
stc ; Set carry on checksum error
jmp .done
.checksum_ok:
clc ; Clear carry on success
.done:
pop di
pop bx
pop ax
ret
; ----------------------------------------------------------------
; CHS Disk Read Routine
; AL = number of sectors
; CL = starting sector (1-based)
; SI = destination offset (Segment:ES already set)
; Inputs:
; AL = sector count
; CH = cylinder
; DH = head
; CL = sector (163, with top 2 bits as high cylinder bits)
; SI = destination offset (segment ES must be set)
; ----------------------------------------------------------------
; Convert LBA to CHS
; Inputs:
; AX = LBA sector number (0-based)
; Outputs:
; CH = cylinder
; DH = head
; CL = sector (1-63, top 2 bits are upper cylinder bits)
lba_to_chs:
pusha
xor dx, dx
mov bx, [sectors_per_track]
div bx ; AX = LBA / sectors_per_track, DX = remainder (sector number)
mov si, ax ; SI = temp quotient (track index)
mov cx, [heads_per_cylinder]
xor dx, dx
div cx ; AX = cylinder, DX = head
mov ch, al ; CH = cylinder low byte
mov dh, dl ; DH = head
; Now take sector number from earlier remainder
mov cx, si ; Copy track index to CX to access CL
and cl, 0x3F ; Mask to 6 bits (sector number)
inc cl ; Sector numbers are 1-based
; Insert upper 2 bits of cylinder into CL
mov ah, al ; AH = cylinder again
and ah, 0xC0 ; Get top 2 bits of cylinder
or cl, ah ; OR them into sector byte
popa
ret
read_chs:
pusha
push dx
mov cx, 5
.retry:
mov ah, 0x02 ; BIOS: Read sectors
mov dl, [bootdev] ; Boot device
; Assume CH, DH, CL already set before this call
int 0x13
jc .error
pop dx
popa
ret
.error:
dec cx
jz disk_error
xor ah, ah
int 0x13
jmp .retry
; ----------------------------------------------------------------
enable_a20:
; Try fast A20 gate method
in al, 0x92
or al, 0x02
and al, 0xFE ; Clear bit 0 to avoid fast A20 bugs
out 0x92, al
; Verify A20
call check_a20
jnc .done ; Success
; Fallback: use keyboard controller method
call .fallback
.done:
ret
.fallback:
mov al, 0xAD ; Disable keyboard
out 0x64, al
call .wait_input_clear
mov al, 0xD0 ; Command: read output port
out 0x64, al
call .wait_output_full
in al, 0x60
or al, 0x02 ; Set A20 enable bit
mov bl, al
call .wait_input_clear
mov al, 0xD1 ; Command: write output port
out 0x64, al
call .wait_input_clear
mov al, bl
out 0x60, al
call .wait_input_clear
mov al, 0xAE ; Enable keyboard
out 0x64, al
ret
.wait_input_clear:
in al, 0x64
test al, 0x02
jnz .wait_input_clear
ret
.wait_output_full:
in al, 0x64
test al, 0x01
jz .wait_output_full
ret
check_a20:
in al, 0x64 ; Read keyboard controller status
test al, 0x02 ; Check if input buffer is full
jnz check_a20 ; Wait until empty
mov al, 0xD0 ; Command: read output port
out 0x64, al
.wait_output:
in al, 0x60 ; Read output port value
test al, 0x02 ; Check A20 gate bit (bit 1)
jnz .a20_enabled ; If set, A20 enabled
xor al, al ; Clear carry to indicate failure
stc ; Set carry for failure, jc will jump
ret
.a20_enabled:
clc ; Clear carry flag to indicate success
ret
; ----------------------------------------------------------------
gdt_start:
dq 0x0000000000000000 ; Null descriptor
dq 0x00CF9A000000FFFF ; 32-bit code segment (selector 0x08)
dq 0x00CF92000000FFFF ; 32-bit data segment (selector 0x10)
dq 0x00009A000000FFFF ; 16-bit code segment for real mode (selector 0x18)
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
gdt_end:
setup_gdt:
lgdt [gdt_descriptor]
ret
; ----------------------------------------------------------------
switch_to_pm:
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:0x7E00
; ----------------------------------------------------------------
print_string_16:
.loop:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp .loop
.done:
ret
disk_error_msg db "Disk error!", 0
a20_error_msg db "A20 error!", 0
halt:
cli
hlt
bootdev db 0
sectors_per_track dw 63
heads_per_cylinder dw 255
times 510 - ($ - $$) db 0
dw 0xAA55

203
bootloader/boot1.asm Normal file
View File

@@ -0,0 +1,203 @@
; ==============================================================================
; boot1.asm - Second Stage Bootloader (Fixed Real Mode Transition)
; ==============================================================================
[BITS 32]
global _start
extern kmain
_start:
; Set up segments
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Stack (must be identity-mapped)
mov esp, 0x90000
; CPU Feature Detection: check CPUID support
pushfd ; Save flags
pop eax
mov ecx, eax
xor eax, 1 << 21 ; Flip ID bit
push eax
popfd
pushfd
pop eax
xor eax, ecx
jz .no_cpuid ; CPUID unsupported if no change
; CPUID supported, verify features
mov eax, 1
cpuid
; Check for paging support (bit 31 of edx)
test edx, 1 << 31
jz .no_paging_support
; Additional CPU feature checks could be added here
jmp .cpuid_check_done
.no_cpuid:
mov si, no_cpuid_msg
call print_string_16
jmp halt
.no_paging_support:
mov si, no_paging_msg
call print_string_16
jmp halt
.cpuid_check_done:
; Temporarily switch back to real mode
cli
mov eax, cr0
and eax, 0x7FFFFFFE ; Clear PE & PG bits
mov cr0, eax
jmp 0x18:real_mode_entry
; ----------------------------------------------------------------
[BITS 16]
real_mode_entry:
; Real mode for BIOS access (E820, VESA)
xor ax, ax
mov es, ax
; VESA call
mov di, VbeControllerInfo
mov ax, 0x4F00
int 0x10
jc vesa_error
; E820 memory map
xor ebx, ebx
mov edx, 0x534D4150
mov di, MemoryMapBuffer
mov [MemoryMapEntries], dword 0
.e820_loop:
mov eax, 0xE820
mov ecx, 24
int 0x15
jc e820_error
add di, 24
inc dword [MemoryMapEntries]
test ebx, ebx
jnz .e820_loop
jmp e820_done
e820_error:
mov si, e820_error_msg
call print_string_16
jmp halt
vesa_error:
mov si, vesa_error_msg
call print_string_16
; Fallback: set VGA text mode 3 and continue
mov ah, 0x00 ; BIOS Set Video Mode function
mov al, 0x03 ; VGA 80x25 text mode
int 0x10
; Clear screen
mov ah, 0x06 ; Scroll up function
mov al, 0 ; Clear entire screen
mov bh, 0x07 ; Text attribute (gray on black)
mov cx, 0 ; Upper-left corner
mov dx, 0x184F ; Lower-right corner
int 0x10
jmp e820_done ; Continue booting without VESA graphics
e820_done:
; Back to protected mode
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:protected_entry
; ----------------------------------------------------------------
[BITS 16]
print_string_16:
.loop:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp .loop
.done:
ret
e820_error_msg db "E820 Failed!", 0
vesa_error_msg db "VESA Failed!", 0
no_cpuid_msg db "No CPUID support detected!", 0
no_paging_msg db "CPU lacks paging support!", 0
; ----------------------------------------------------------------
[BITS 32]
protected_entry:
; Paging setup
xor eax, eax
mov edi, page_directory
mov ecx, 1024
rep stosd
mov edi, page_table
rep stosd
mov eax, page_table
or eax, 0x3
mov [page_directory], eax
mov ecx, 1024
mov edi, page_table
mov eax, 0x00000003
.fill_pages:
mov [edi], eax
add eax, 0x1000
add edi, 4
loop .fill_pages
mov eax, page_directory
mov cr3, eax
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
jmp kmain
halt:
cli
.hang:
hlt
jmp .hang
; ----------------------------------------------------------------
; Data buffers and variables must be appropriately defined in your data section
MemoryMapBuffer times 128 db 0 ; 128*24 bytes reserved for E820 memory map (adjust size as needed)
MemoryMapEntries dd 0
VbeControllerInfo times 512 db 0 ; Buffer for VESA controller info (adjust size as needed)
; Define page directory and page table aligned as needed (in your data section)
align 4096
page_directory times 1024 dd 0
align 4096
page_table times 1024 dd 0
%assign pad_size 4096
%ifdef __SIZE__
%define size_current __SIZE__
%else
%define size_current ($ - $$)
%endif
%if size_current < pad_size
times pad_size - size_current db 0
%endif
checksum_byte db 0

26
bootloader/linker.ld Normal file
View File

@@ -0,0 +1,26 @@
ENTRY(kmain)
SECTIONS {
. = 1M;
.text : {
*(.text*)
}
.rodata : { *(.rodata*) }
.data : { *(.data*) }
.bss : {
*(.bss*)
*(COMMON)
}
.stack (NOLOAD) : {
. = ALIGN(4);
. = . + 0x1000;
}
.heap (NOLOAD) : {
. = ALIGN(4);
. = . + 0x10000;
}
}

View File

@@ -1,44 +0,0 @@
#!/bin/bash
# Exit immediately if a command exits with a non-zero status.
set -e
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check for required commands
for cmd in nasm gcc qemu-system-i386; do
if ! command_exists "$cmd"; then
echo "Error: $cmd is not installed or not in PATH" >&2
exit 1
fi
done
# Compile the assembly file
echo "Compiling boot.asm..."
nasm -f elf32 boot.asm -o boot.o
# Compile and link the kernel
echo "Compiling and linking kernel..."
gcc -m32 -ffreestanding -nostdlib -fno-pic -fno-pie kernel.c boot.o -o kernel.bin -T linker.ld
# Check if compilation was successful
if [ -f kernel.bin ]; then
echo "Build successful. kernel.bin created."
# Ask user if they want to run QEMU
read -p "Do you want to run QEMU now? (y/n) " -n 1 -r
echo # Move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Running QEMU..."
qemu-system-i386 -enable-kvm -net none -fda kernel.bin
else
echo "QEMU not started. You can run it later with:"
echo "qemu-system-i386 -enable-kvm -net none -fda kernel.bin"
fi
else
echo "Build failed. Check for errors above." >&2
exit 1
fi

8
disk/Makefile Normal file
View File

@@ -0,0 +1,8 @@
IMG_SIZE = 1440k
BOOT_BIN = ../build/boot.bin
KERNEL_BIN = ../build/kernel.bin
DISK_IMG = ../build/disk.img
$(DISK_IMG): $(BOOT_BIN) $(KERNEL_BIN)
dd if=$(BOOT_BIN) of=$@ bs=512 seek=4
dd if=$(KERNEL_BIN) of=$@ bs=512 seek=200

View File

@@ -1,13 +0,0 @@
#include <stdint.h>
void kmain(void)
{
const uint16_t color = 0x0F00;
const char *hello = "Hello C world!";
volatile uint16_t *vga = (volatile uint16_t *)0xb8000;
for (int i = 0; i < 16; ++i)
{
vga[i + 80] = color | (uint16_t)hello[i];
}
}

54
kernel/acpi.c Normal file
View File

@@ -0,0 +1,54 @@
#include "acpi.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
// Function to find the RSDP (Root System Description Pointer)
acpi_rsdp_t* acpi_find_rsdp() {
// Search memory from 0x000E0000 to 0x00100000 (BIOS)
for (uint32_t addr = 0x000E0000; addr < 0x00100000; addr += 16) {
acpi_rsdp_t* rsdp = (acpi_rsdp_t*)addr;
if (memcmp(rsdp->signature, "RSD PTR ", 8) == 0) {
uint8_t checksum = 0;
for (size_t i = 0; i < sizeof(acpi_rsdp_t); i++) { // Change int to size_t
checksum += ((uint8_t*)rsdp)[i];
}
if (checksum == 0) {
return rsdp; // Valid RSDP found
}
}
}
return NULL; // RSDP not found
}
// Function to get the RSDT or XSDT based on the RSDP revision
void* acpi_get_rsdt_or_xsdt(acpi_rsdp_t* rsdp) {
if (rsdp->revision >= 2) {
return (void*)rsdp->xsdt_addr; // ACPI 2.0+ uses XSDT
} else {
return (void*)rsdp->rsdt_addr; // ACPI 1.0 uses RSDT
}
}
// Function to find the FADT table within the RSDT or XSDT
acpi_fadt_t* acpi_find_fadt(void* rsdt_or_xsdt) {
acpi_rsdt_t* rsdt = (acpi_rsdt_t*)rsdt_or_xsdt;
uint32_t num_tables = (rsdt->length - sizeof(acpi_rsdt_t)) / sizeof(uint32_t);
for (size_t i = 0; i < num_tables; i++) {
uint32_t table_addr = rsdt->tables[i];
acpi_fadt_t* fadt = (acpi_fadt_t*)table_addr;
if (fadt->signature == 0x50434146) {
uint8_t checksum = 0;
for (size_t j = 0; j < fadt->length; j++) {
checksum += ((uint8_t*)fadt)[j];
}
if (checksum == 0) {
return fadt;
}
}
}
return NULL;
}

47
kernel/acpi.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef ACPI_H
#define ACPI_H
#include <stdint.h>
// ACPI base address (replace with actual value)
#define ACPI_BASE 0xE0000000
#ifndef NULL
#define NULL ((void*)0)
#endif
// ACPI RSDP Structure (Root System Description Pointer)
typedef struct {
uint8_t signature[8]; // Should be "RSD PTR "
uint8_t checksum; // Checksum for the RSDP structure
uint8_t oem_id[6]; // OEM ID string
uint8_t revision; // ACPI revision
uint32_t rsdt_addr; // 32-bit RSDT address (ACPI 1.0)
uint32_t xsdt_addr; // 64-bit XSDT address (ACPI 2.0+)
} __attribute__((packed)) acpi_rsdp_t;
// ACPI RSDT Structure (Root System Description Table)
typedef struct {
uint32_t signature; // Should be "RSDT"
uint32_t length; // Length of the table
uint8_t revision; // Revision of the RSDT table
uint8_t checksum; // Checksum for the RSDT table
uint32_t tables[]; // Array of pointers to other tables (RSDT/XSDT entries)
} __attribute__((packed)) acpi_rsdt_t;
// ACPI FADT Structure (Fixed ACPI Description Table)
typedef struct {
uint32_t signature; // Should be "FACP"
uint32_t length; // Length of the table
uint8_t revision; // Revision of the FADT table
uint8_t checksum; // Checksum for the FADT table
uint32_t pm_tmr_address; // Power Management Timer Address
// ... other FADT fields
} __attribute__((packed)) acpi_fadt_t;
// Function prototypes
acpi_rsdp_t* acpi_find_rsdp();
void* acpi_get_rsdt_or_xsdt(acpi_rsdp_t* rsdp);
acpi_fadt_t* acpi_find_fadt(void* rsdt_or_xsdt);
#endif /* ACPI_H */

37
kernel/cpu.c Normal file
View File

@@ -0,0 +1,37 @@
#include "cpu.h"
#include "serial.h"
#include "terminal.h"
#include "utils.h"
#include "print.h"
void cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
__asm__(
"cpuid"
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
: "a"(function)
);
}
void identify_cpu() {
uint32_t eax, ebx, ecx, edx;
char vendor[13];
cpuid(0, &eax, &ebx, &ecx, &edx);
*(uint32_t *)&vendor[0] = ebx;
*(uint32_t *)&vendor[4] = edx;
*(uint32_t *)&vendor[8] = ecx;
vendor[12] = '\0';
terminal_write("CPU Vendor: ");
terminal_write(vendor);
terminal_write("\n");
serial_write("CPU Vendor: ");
serial_write(vendor);
serial_write("\n");
terminal_write("CPUID max leaf: ");
print_hex(eax, false, false); // You must implement this (see below)
terminal_write("\n");
}

9
kernel/cpu.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef CPU_H
#define CPU_H
#include <stdint.h>
void cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
void identify_cpu(void);
#endif // CPU_H

45
kernel/debug.c Normal file
View File

@@ -0,0 +1,45 @@
#include "debug.h"
#include "vga.h"
#include <stdint.h>
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
#define VGA_MEMORY 0xB8000
// VGA text mode color attributes
#define COLOR_WHITE 0x07
// Pointer to the VGA memory
volatile uint16_t* vga_buffer = (uint16_t*)VGA_MEMORY;
// Function to print a string to the VGA text buffer
void debug_print(const char *str) {
while (*str) {
if (*str == '\n') {
// Handle new line
// Move to the next line (not implemented here)
// You can implement line wrapping if needed
str++;
continue;
}
// Calculate the position in the VGA buffer
static int cursor_x = 0;
static int cursor_y = 0;
// Write the character and its attribute to the VGA buffer
vga_buffer[cursor_y * VGA_WIDTH + cursor_x] = (COLOR_WHITE << 8) | *str;
// Move the cursor to the right
cursor_x++;
if (cursor_x >= VGA_WIDTH) {
cursor_x = 0;
cursor_y++;
if (cursor_y >= VGA_HEIGHT) {
cursor_y = 0; // Scroll up (not implemented here)
}
}
str++;
}
}

6
kernel/debug.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef DEBUG_H
#define DEBUG_H
void debug_print(const char *str);
#endif // DEBUG_H

36
kernel/display.c Normal file
View File

@@ -0,0 +1,36 @@
#include "display.h"
#include "io.h" // Include your I/O header for port access
#include "vga.h"
// Initialize the display
void init_display(void) {
// Initialize VGA settings, if necessary
// This could involve setting up the VGA mode, etc.
set_display_mode(0x13); // Example: Set to 320x200 256-color mode
}
// Enumerate connected displays
void enumerate_displays(void) {
// This is a simplified example. Actual enumeration may require
// reading from specific VGA registers or using BIOS interrupts.
// For demonstration, we will just print a message
// In a real driver, you would check the VGA registers
// to determine connected displays.
clear_display();
// Here you would typically read from VGA registers to find connected displays
// For example, using inb() to read from VGA ports
}
// Set the display mode
void set_display_mode(uint8_t mode) {
// Set the VGA mode by writing to the appropriate registers
outb(VGA_PORT, mode); // Example function to write to a port
}
// Clear the display
void clear_display(void) {
// Clear the display by filling it with a color
// This is a placeholder for actual clearing logic
// You would typically write to video memory here
}

14
kernel/display.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef DISPLAY_H
#define DISPLAY_H
#include <stdint.h>
#define VGA_PORT 0x3C0 // Base port for VGA
// Function prototypes
void init_display(void);
void enumerate_displays(void);
void set_display_mode(uint8_t mode);
void clear_display(void);
#endif // DISPLAY_H

50
kernel/elf.c Normal file
View File

@@ -0,0 +1,50 @@
#include "elf.h"
#include <stddef.h>
#include <string.h>
// This assumes that the elf is ELF32 and little-endian Intel X86 architecture.
// Check if a binary is a valid ELF executable.
int elf_validate(const void* data) {
const Elf32_Ehdr* header = (const Elf32_Ehdr*)data;
if (*(uint32_t*)header->e_ident != ELF_MAGIC)
return 0;
if (header->e_type != ET_EXEC && header->e_type != ET_DYN)
return 0;
if (header->e_machine != EM_386)
return 0;
return 1;
}
// Load an ELF executable into memory.
int elf_load(const void* data, void (*load_segment)(uint32_t vaddr, const void* src, uint32_t size)) {
const Elf32_Ehdr* header = (const Elf32_Ehdr*)data;
const Elf32_Phdr* ph = (const Elf32_Phdr*)((uint8_t*)data + header->e_phoff);
for (int i = 0; i < header->e_phnum; i++) {
if (ph[i].p_type != PT_LOAD)
continue;
const void* src = (uint8_t*)data + ph[i].p_offset;
uint32_t vaddr = ph[i].p_vaddr;
uint32_t filesz = ph[i].p_filesz;
uint32_t memsz = ph[i].p_memsz;
// Copy data segment
load_segment(vaddr, src, filesz);
// Zero remaining BSS (if any)
if (memsz > filesz) {
uint8_t* bss_start = (uint8_t*)(vaddr + filesz);
for (uint32_t j = 0; j < memsz - filesz; j++) {
bss_start[j] = 0;
}
}
}
return header->e_entry;
}

52
kernel/elf.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef ELF_H
#define ELF_H
#include <stdint.h>
#define ELF_MAGIC 0x464C457F // "\x7FELF" in little-endian
// ELF Types
#define ET_EXEC 2
#define ET_DYN 3
// ELF Machine
#define EM_386 3
// ELF Ident indexes
#define EI_NIDENT 16
// Program header types
#define PT_NULL 0
#define PT_LOAD 1
// ELF Header (32-bit)
typedef struct {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry; // Entry point
uint32_t e_phoff; // Program header table offset
uint32_t e_shoff; // Section header table offset
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} __attribute__((packed)) Elf32_Ehdr;
// Program Header (32-bit)
typedef struct {
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} __attribute__((packed)) Elf32_Phdr;
#endif // ELF_H

5
kernel/fat12.c Normal file
View File

@@ -0,0 +1,5 @@
#include "fat12.h"
void fat12_init() {
// Filesystem initialization code
}

47
kernel/fat12.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef FAT12_H
#define FAT12_H
#include <stdint.h> /* Include standard integer types */
#include <stdio.h> /* Include standard I/O library */
#include <stdlib.h> /* Include standard library */
#define FAT12_SECTOR_SIZE 512 /* Sector size for FAT12 */
#define FAT12_MAX_FILES 128 /* Maximum number of files in root directory */
#define FAT12_ROOT_DIR_SECTORS 1 /* Number of sectors for root directory */
typedef struct {
uint8_t jump[3]; /* Jump instruction for boot */
char oem[8]; /* OEM name */
uint16_t bytes_per_sector; /* Bytes per sector */
uint8_t sectors_per_cluster; /* Sectors per cluster */
uint16_t reserved_sectors; /* Reserved sectors count */
uint8_t num_fats; /* Number of FATs */
uint16_t max_root_dir_entries; /* Max entries in root directory */
uint16_t total_sectors; /* Total sectors */
uint8_t media_descriptor; /* Media descriptor */
uint16_t fat_size; /* Size of each FAT */
uint16_t sectors_per_track; /* Sectors per track */
uint16_t num_heads; /* Number of heads */
uint32_t hidden_sectors; /* Hidden sectors count */
uint32_t total_sectors_large; /* Total sectors for large disks */
} __attribute__((packed)) FAT12_BootSector; /* Packed structure for boot sector */
typedef struct {
char name[11]; /* File name (8.3 format) */
uint8_t attr; /* File attributes */
uint16_t reserved; /* Reserved */
uint16_t time; /* Time of last write */
uint16_t date; /* Date of last write */
uint16_t start_cluster; /* Starting cluster number */
uint32_t file_size; /* File size in bytes */
} __attribute__((packed)) FAT12_DirEntry; /* Directory entry structure */
void initialize_fat12(const char *disk_image); /* Function to initialize FAT12 */
void read_fat12(const char *disk_image); /* Function to read FAT12 */
void write_fat12(const char *disk_image); /* Function to write FAT12 */
void list_files(const char *disk_image); /* Function to list files in root directory */
void read_file(const char *disk_image, const char *filename); /* Function to read a file */
void write_file(const char *disk_image, const char *filename, const uint8_t *data, size_t size); /* Function to write a file */
#endif
/* FAT12_H */

0
kernel/framebuffer.c Normal file
View File

0
kernel/framebuffer.h Normal file
View File

21
kernel/gdt.asm Normal file
View File

@@ -0,0 +1,21 @@
; gdt.asm
; Assembler function to load the GDT and update segment registers
global gdt_flush
gdt_flush:
mov eax, [esp + 4] ; Argument: pointer to GDT descriptor
lgdt [eax] ; Load GDT
; Reload segment registers
mov ax, 0x10 ; 0x10 = offset to kernel data segment (3rd entry)
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Jump to flush the instruction pipeline and load CS
jmp 0x08:.flush ; 0x08 = offset to code segment (2nd entry)
.flush:
ret

48
kernel/gdt.c Normal file
View File

@@ -0,0 +1,48 @@
#include "gdt.h"
// Structure of a GDT entry (8 bytes)
struct gdt_entry {
uint16_t limit_low; // Limit bits 015
uint16_t base_low; // Base bits 015
uint8_t base_middle; // Base bits 1623
uint8_t access; // Access flags
uint8_t granularity; // Granularity + limit bits 1619
uint8_t base_high; // Base bits 2431
} __attribute__((packed));
// Structure of the GDT pointer
struct gdt_ptr {
uint16_t limit;
uint32_t base;
} __attribute__((packed));
// Declare GDT with 3 entries
static struct gdt_entry gdt[3];
static struct gdt_ptr gp;
// External ASM function to load GDT
extern void gdt_flush(uint32_t);
// Set an individual GDT entry
static void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t granularity) {
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = ((limit >> 16) & 0x0F) | (granularity & 0xF0);
gdt[num].access = access;
}
// Initialize the GDT
void gdt_init(void) {
gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
gp.base = (uint32_t)&gdt;
gdt_set_gate(0, 0, 0, 0, 0); // Null segment
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
gdt_flush((uint32_t)&gp);
}

8
kernel/gdt.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef GDT_H
#define GDT_H
#include <stdint.h>
void gdt_init(void);
#endif

0
kernel/gui.c Normal file
View File

0
kernel/gui.h Normal file
View File

63
kernel/heap.c Normal file
View File

@@ -0,0 +1,63 @@
#include "heap.h"
#include <stdint.h>
typedef struct heap_block {
size_t size;
struct heap_block *next;
int is_free;
} heap_block_t;
static heap_block_t *free_list = NULL;
static void *heap_start_ptr = NULL;
static void *heap_end_ptr = NULL;
void heap_init(void *heap_start, void *heap_end) {
heap_start_ptr = heap_start;
heap_end_ptr = heap_end;
free_list = (heap_block_t *)heap_start;
free_list->size = (uintptr_t)heap_end - (uintptr_t)heap_start - sizeof(heap_block_t);
free_list->next = NULL;
free_list->is_free = 1;
}
void *heap_alloc(size_t size) {
heap_block_t *current = free_list;
while (current != NULL) {
if (current->is_free && current->size >= size) {
// If it's a large block, split it
if (current->size > size + sizeof(heap_block_t)) {
heap_block_t *new_block = (heap_block_t *)((uintptr_t)current + sizeof(heap_block_t) + size);
new_block->size = current->size - size - sizeof(heap_block_t);
new_block->next = current->next;
new_block->is_free = 1;
current->next = new_block;
current->size = size;
}
current->is_free = 0;
return (void *)((uintptr_t)current + sizeof(heap_block_t));
}
current = current->next;
}
return NULL; // Out of memory
}
void heap_free(void *ptr) {
if (ptr == NULL) return;
heap_block_t *block = (heap_block_t *)((uintptr_t)ptr - sizeof(heap_block_t));
block->is_free = 1;
// Coalesce with next block
if (block->next && block->next->is_free) {
block->size += block->next->size + sizeof(heap_block_t);
block->next = block->next->next;
}
// TODO: Coalesce with previous block for better compaction
}

11
kernel/heap.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef HEAP_H
#define HEAP_H
#include <stddef.h>
void heap_init(void *heap_start, void *heap_end);
void *heap_alloc(size_t size);
void heap_free(void *ptr);
#endif // HEAP_H

105
kernel/idt.c Normal file
View File

@@ -0,0 +1,105 @@
#include "idt.h"
#include "io.h"
#define KERNEL_CS 0x08 // Kernel code segment selector
idt_entry_t idt[IDT_ENTRIES];
idt_ptr_t idt_ptr;
// External assembly stubs for ISRs (provided below)
extern void isr0();
extern void isr1();
extern void isr2();
extern void isr3();
extern void isr4();
extern void isr5();
extern void isr6();
extern void isr7();
extern void isr8();
extern void isr9();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();
extern void isr_default();
// Helper to set an IDT gate
void idt_set_gate(int n, uint32_t handler) {
idt[n].offset_low = handler & 0xFFFF;
idt[n].selector = KERNEL_CS;
idt[n].zero = 0;
idt[n].type_attr = 0x8E; // Present, ring 0, 32-bit interrupt gate
idt[n].offset_high = (handler >> 16) & 0xFFFF;
}
// Load IDT via lidt
static void idt_load() {
__asm__("lidt (%0)" : : "r" (&idt_ptr));
}
// IDT initialization
void idt_init() {
idt_ptr.limit = sizeof(idt_entry_t) * IDT_ENTRIES - 1;
idt_ptr.base = (uint32_t)&idt;
// Clear all entries
for (int i = 0; i < IDT_ENTRIES; i++) {
idt_set_gate(i, (uint32_t)isr_default);
}
// Set specific handlers
// Assign CPU exception handlers
idt_set_gate(0, (uint32_t)isr0);
idt_set_gate(1, (uint32_t)isr1);
idt_set_gate(2, (uint32_t)isr2);
idt_set_gate(3, (uint32_t)isr3);
idt_set_gate(4, (uint32_t)isr4);
idt_set_gate(5, (uint32_t)isr5);
idt_set_gate(6, (uint32_t)isr6);
idt_set_gate(7, (uint32_t)isr7);
idt_set_gate(8, (uint32_t)isr8);
idt_set_gate(9, (uint32_t)isr9);
idt_set_gate(10, (uint32_t)isr10);
idt_set_gate(11, (uint32_t)isr11);
idt_set_gate(12, (uint32_t)isr12);
idt_set_gate(13, (uint32_t)isr13);
idt_set_gate(14, (uint32_t)isr14);
idt_set_gate(15, (uint32_t)isr15);
idt_set_gate(16, (uint32_t)isr16);
idt_set_gate(17, (uint32_t)isr17);
idt_set_gate(18, (uint32_t)isr18);
idt_set_gate(19, (uint32_t)isr19);
idt_set_gate(20, (uint32_t)isr20);
idt_set_gate(21, (uint32_t)isr21);
idt_set_gate(22, (uint32_t)isr22);
idt_set_gate(23, (uint32_t)isr23);
idt_set_gate(24, (uint32_t)isr24);
idt_set_gate(25, (uint32_t)isr25);
idt_set_gate(26, (uint32_t)isr26);
idt_set_gate(27, (uint32_t)isr27);
idt_set_gate(28, (uint32_t)isr28);
idt_set_gate(29, (uint32_t)isr29);
idt_set_gate(30, (uint32_t)isr30);
idt_set_gate(31, (uint32_t)isr31);
idt_load();
}

24
kernel/idt.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
#define IDT_ENTRIES 256
typedef struct {
uint16_t offset_low;
uint16_t selector;
uint8_t zero;
uint8_t type_attr;
uint16_t offset_high;
} __attribute__((packed)) idt_entry_t;
typedef struct {
uint16_t limit;
uint32_t base;
} __attribute__((packed)) idt_ptr_t;
void idt_set_gate(int n, uint32_t handler);
void idt_init(void);
#endif

16
kernel/io.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef IO_H
#define IO_H
#include <stdint.h>
static inline void outb(uint16_t port, uint8_t val) {
__asm__("outb %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint8_t inb(uint16_t port) {
uint8_t ret;
__asm__("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
#endif

5
kernel/irq.c Normal file
View File

@@ -0,0 +1,5 @@
#include "irq.h"
void irq_init() {
// IRQ initialization code
}

6
kernel/irq.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef IRQ_H
#define IRQ_H
void irq_init();
#endif // IRQ_H

80
kernel/isr.asm Normal file
View File

@@ -0,0 +1,80 @@
[BITS 32]
GLOBAL isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8, isr9
GLOBAL isr10, isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19
GLOBAL isr20, isr21, isr22, isr23, isr24, isr25, isr26, isr27, isr28, isr29
GLOBAL isr30, isr31, isr_default
[EXTERN isr_handler]
%macro ISR_NOERR 1
isr%1:
cli
push dword 0 ; Dummy error code
push dword %1 ; Interrupt number
call isr_handler
add esp, 8
sti
iret
%endmacro
%macro ISR_ERR 1
isr%1:
cli
push dword %1 ; Interrupt number
call isr_handler
add esp, 8
sti
iret
%endmacro
; ISR 07: No error code
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 814: Error code pushed automatically
ISR_ERR 8
ISR_NOERR 9 ; Coprocessor Segment Overrun (obsolete, no error code)
ISR_ERR 10
ISR_ERR 11
ISR_ERR 12
ISR_ERR 13
ISR_ERR 14
; ISR 15 is reserved
ISR_NOERR 15
; ISR 1619 (FPU, Alignment Check, etc.)
ISR_NOERR 16
ISR_ERR 17
ISR_NOERR 18
ISR_NOERR 19
; ISR 2031 (reserved or future use)
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_NOERR 30
ISR_NOERR 31
; Fallback handler
isr_default:
cli
push dword 255
push dword 0
call isr_handler
add esp, 8
sti
iret

53
kernel/isr.c Normal file
View File

@@ -0,0 +1,53 @@
#include "terminal.h"
#include "serial.h"
#include "isr.h"
#include "io.h"
#include "print.h"
static isr_callback_t interrupt_handlers[MAX_INTERRUPTS] = { 0 };
void isr_handler(uint32_t int_num, uint32_t err_code) {
terminal_write("Interrupt occurred: ");
print_hex(int_num, true, false);
terminal_write("\n");
serial_write("INT triggered\n");
terminal_write("Error code: ");
print_hex(err_code, true, false);
terminal_write("\n");
if (interrupt_handlers[int_num]) {
interrupt_handlers[int_num](); // Call registered handler
} else {
terminal_write(" -> No handler registered\n");
if (int_num == 0) {
terminal_write(" -> Divide by zero error!\n");
} else if (int_num == 13) {
terminal_write(" -> General Protection Fault!\n");
} else {
terminal_write(" -> Unknown interrupt\n");
}
// Halt CPU
while (1) {
__asm__("hlt");
}
}
// === Send End Of Interrupt to PIC(s) ===
if (int_num >= 40) {
// Send reset signal to slave PIC
outb(0xA0, 0x20);
}
if (int_num >= 32) {
// Send reset signal to master PIC
outb(0x20, 0x20);
}
}
void register_interrupt_handler(uint8_t n, isr_callback_t handler) {
interrupt_handlers[n] = handler;
}

13
kernel/isr.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef ISR_H
#define ISR_H
#include <stdint.h>
#define MAX_INTERRUPTS 256
typedef void (*isr_callback_t)(void);
void isr_handler(uint32_t int_num, uint32_t err_code);
void register_interrupt_handler(uint8_t n, isr_callback_t handler);
#endif

59
kernel/keyboard.c Normal file
View File

@@ -0,0 +1,59 @@
#include "keyboard.h"
#include "io.h"
#include "isr.h"
#include "terminal.h"
#define KEYBOARD_DATA_PORT 0x60
static char key_buffer[256];
static uint8_t buffer_index = 0;
// Basic US QWERTY keymap (scancode to ASCII)
static const char scancode_map[128] = {
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', // 0x00 - 0x09
'9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', // 0x0A - 0x13
't', 'y', 'z', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x14 - 0x1D
0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', // 0x1E - 0x27
';', '\'', '`', 0, '\\', 'x', 'c', 'v', 'b', // 0x28 - 0x31
'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, // 0x32 - 0x3B
// rest can be filled as needed
};
// Interrupt handler for IRQ1
void keyboard_callback(void) {
uint8_t scancode = inb(0x60);
// Only handle key press (ignore key release)
if (!(scancode & 0x80)) {
char c = scancode_map[scancode];
if (c && buffer_index < sizeof(key_buffer) - 1) {
key_buffer[buffer_index++] = c;
terminal_putchar(c);
}
}
// Send End of Interrupt (EOI) to the PIC
outb(0x20, 0x20);
}
void keyboard_init() {
register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21)
}
// Blocking read (returns one char)
char keyboard_get_char() {
while (buffer_index == 0); // Busy wait
char c;
__asm__ __volatile__("cli");
c = key_buffer[0];
for (uint8_t i = 1; i < buffer_index; i++) {
key_buffer[i - 1] = key_buffer[i];
}
buffer_index--;
__asm__ __volatile__("sti");
return c;
}

7
kernel/keyboard.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef KEYBOARD_H
#define KEYBOARD_H
void keyboard_init(void);
char keyboard_get_char(void); // Blocking read from buffer
#endif

87
kernel/kmain.c Normal file
View File

@@ -0,0 +1,87 @@
#include <stdint.h>
#include "io.h"
#include "serial.h"
#include "terminal.h"
#include "idt.h"
#include "paging.h"
#include "memmap.h"
#include "gdt.h"
#include "cpu.h"
#include "kmalloc.h"
#include "print.h"
#include "timer.h"
#include "utils.h"
#include "keyboard.h"
#define LPT1 0x378
void lpt_write(char c) {
while ((inb(LPT1 + 1) & 0x80) == 0); // Wait for ready
outb(LPT1, c);
}
void kmain(void) {
terminal_initialize();
terminal_write("Welcome to ClassicOS\n");
serial_init();
serial_write("Serial port initialized.\n");
terminal_write("Identifying CPU...\n");
identify_cpu();
serial_write("CPU identification complete.\n");
lpt_write('L'); // Send 'L' to LPT1 to test
terminal_write("Initializing GDT...\n");
gdt_init();
serial_write("GDT initialized.\n");
terminal_write("Initializing IDT...\n");
idt_init();
serial_write("IDT initialized.\n");
terminal_write("Enabling paging...\n");
paging_init();
serial_write("Paging initialized.\n");
terminal_write("Initializing memory allocator...\n");
kmalloc_init(0xC0100000); // Virtual heap start address (must be mapped!)
serial_write("kmalloc initialized.\n");
serial_write("Allocated 128 bytes.\n");
terminal_write("Initializing timer...\n");
timer_init(100); // 100 Hz (10 ms interval)
serial_write("Timer initialized.\n");
terminal_write("Initializing keyboard...\n");
keyboard_init();
serial_write("Keyboard initialized.\n");
terminal_write("Getting memory map...\n");
memory_map_entry_t mmap[32];
uint32_t mmap_size = get_memory_map(mmap, 32);
serial_write("Memory map retrieved.\n");
terminal_write("Memory Regions:\n");
char buf[32];
for (uint32_t i = 0; i < mmap_size; i++) {
terminal_write(" - Base: ");
print_hex((uint32_t)(mmap[i].base_addr & 0xFFFFFFFF), true, false); // Lower 32 bits
terminal_write(", Length: ");
print_hex((uint32_t)(mmap[i].length & 0xFFFFFFFF), true, false); // Lower 32 bits
terminal_write(", Type: ");
itoa(mmap[i].type, buf, 10);
terminal_write(buf);
terminal_write("\n");
}
terminal_write("System initialized. Halting.\n");
// Halt CPU in loop
while (1) {
__asm__("hlt");
}
}

51
kernel/kmalloc.c Normal file
View File

@@ -0,0 +1,51 @@
#include "kmalloc.h"
#include "terminal.h" // Optional: for debug output
#define HEAP_END 0xC0100000
static uint32_t current_heap = 0;
// Initialize the allocator with a starting heap address
void kmalloc_init(uint32_t heap_start) {
current_heap = heap_start;
}
// Simple bump allocator
void* kmalloc(size_t size) {
if (current_heap == 0) {
terminal_write("kmalloc used before initialization!\n");
return 0;
}
void* addr = (void*)current_heap;
current_heap += size;
return addr;
}
// Allocate memory aligned to a power-of-two boundary (e.g., 0x1000)
void* kmalloc_aligned(size_t size, uint32_t alignment) {
if (current_heap == 0) {
terminal_write("kmalloc_aligned used before initialization!\n");
return 0;
}
// Align the current_heap pointer
if ((current_heap & (alignment - 1)) != 0) {
current_heap = (current_heap + alignment) & ~(alignment - 1);
}
if (current_heap + size > HEAP_END) {
terminal_write("kmalloc_aligned: Out of memory!\n");
return 0;
}
void* addr = (void*)current_heap;
current_heap += size;
return addr;
}
void kfree(void* ptr) {
// In a bump allocator, we cannot free individual blocks.
// We can reset the allocator to the initial state.
current_heap = 0; // Reset the heap pointer
}

11
kernel/kmalloc.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef KMALLOC_H
#define KMALLOC_H
#include <stdint.h>
#include <stddef.h> // for size_t
void kmalloc_init(uint32_t heap_start);
void* kmalloc(size_t size);
void* kmalloc_aligned(size_t size, uint32_t alignment);
#endif

105
kernel/malloc.c Normal file
View File

@@ -0,0 +1,105 @@
#include "malloc.h"
#include <stdint.h>
static void *heap_start; // Start of the heap
static void *heap_end; // End of the heap
static struct memory_block *free_blocks; // List of free blocks
void init_heap(void *start, void *end)
{
heap_start = start;
heap_end = end;
// Initialize the heap with a single large free block
free_blocks = (struct memory_block *)start;
free_blocks->size = (uintptr_t)end - (uintptr_t)start - sizeof(struct memory_block);
free_blocks->next = NULL;
free_blocks->is_free = 1;
}
void *find_free_block(size_t size) {
struct memory_block *current = free_blocks;
while (current != NULL) {
if (current->is_free && current->size >= size) {
return current;
}
current = current->next;
}
// No suitable block found
return NULL;
}
void mark_as_used(void *ptr, size_t size) {
struct memory_block *block = (struct memory_block *)ptr;
block->is_free = 0;
// If the block is larger than needed, split it
if (block->size > size + sizeof(struct memory_block)) {
struct memory_block *new_block = (struct memory_block *)((uintptr_t)ptr + size + sizeof(struct memory_block));
new_block->size = block->size - size - sizeof(struct memory_block);
new_block->next = block->next;
new_block->is_free = 1;
block->size = size;
block->next = new_block;
}
}
void mark_as_free(void *ptr) {
struct memory_block *block = (struct memory_block *)ptr;
block->is_free = 1;
// Coalesce with next block if it's free
if (block->next && block->next->is_free) {
block->size += block->next->size + sizeof(struct memory_block);
block->next = block->next->next;
}
// Coalesce with previous block if it's free
struct memory_block *prev = free_blocks;
while (prev && prev->next != block) {
prev = prev->next;
}
if (prev && prev->is_free) {
prev->size += block->size + sizeof(struct memory_block);
prev->next = block->next;
}
}
void *malloc(size_t size)
{
if (heap_start == NULL || heap_end == NULL)
{
// Heap not initialized, cannot allocate
return NULL;
}
// Align the size to the word size for efficiency
size = (size + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1);
// Search for a free block of sufficient size
void *block = find_free_block(size);
if (block != NULL)
{
// Mark the block as used
mark_as_used(block, size);
return (void *)((uintptr_t)block + sizeof(struct memory_block));
}
// No suitable block found, out of memory
return NULL;
}
void free(void *ptr)
{
if (ptr == NULL)
{
return;
}
// Adjust pointer to the start of the memory block
struct memory_block *block = (struct memory_block *)((uintptr_t)ptr - sizeof(struct memory_block));
// Mark the block as free
mark_as_free(block);
}

27
kernel/malloc.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef MALLOC_H
#define MALLOC_H
#include <stddef.h> // For size_t
// Define the memory block structure
struct memory_block {
size_t size;
struct memory_block *next;
int is_free;
};
// Function prototypes
void init_heap(void *start, void *end);
void *malloc(size_t size);
void free(void *ptr);
// Helper function prototypes
void *find_free_block(size_t size);
void mark_as_used(void *ptr, size_t size);
void mark_as_free(void *ptr);
// External heap boundaries
extern void *user_heap_start;
extern void *user_heap_end;
#endif // MALLOC_H

21
kernel/memmap.c Normal file
View File

@@ -0,0 +1,21 @@
#include "memmap.h"
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) {
uint32_t count = 0;
if (max_entries >= 1) {
map[count].base_addr = 0x00000000;
map[count].length = 0x0009FC00;
map[count].type = 1;
count++;
}
if (max_entries >= 2) {
map[count].base_addr = 0x00100000;
map[count].length = 0x1FF00000;
map[count].type = 1;
count++;
}
return count;
}

14
kernel/memmap.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef MEMMAP_H
#define MEMMAP_H
#include <stdint.h>
typedef struct {
uint64_t base_addr;
uint64_t length;
uint32_t type;
} __attribute__((packed)) memory_map_entry_t;
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries);
#endif

27
kernel/mouse.c Normal file
View File

@@ -0,0 +1,27 @@
// mouse.c
#include "mouse.h"
#include "usb.h"
#include <stdint.h>
#include <stdbool.h>
// Mouse buffer
static mouse_data_t mouse_data;
// Read USB mouse data
mouse_data_t usb_read_mouse(void) {
uint8_t buffer[3]; // USB HID Mouse reports typically use 3 bytes
if (usb_interrupt_transfer()) { // Ensure buffer is filled
// Process the received data
mouse_data.x += buffer[1]; // X movement
mouse_data.y += buffer[2]; // Y movement
mouse_data.left_button = buffer[0] & 0x01;
mouse_data.right_button = buffer[0] & 0x02;
} else {
// Handle the case where no data was received
// You can choose to reset mouse_data or leave it unchanged
// For example, you might want to set movement to zero if no data is received
// mouse_data.x = 0;
// mouse_data.y = 0;
}
return mouse_data;
}

26
kernel/mouse.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef MOUSE_H
#define MOUSE_H
#include <stdint.h>
#include <stdbool.h>
// Mouse data structure
typedef struct {
int x;
int y;
bool left_button;
bool right_button;
} mouse_data_t;
// Function declarations for USB 1.x HID mouse support
bool usb_mouse_init(void);
bool usb_mouse_detected(void);
bool usb_mouse_received(void);
mouse_data_t usb_read_mouse(void);
// USB Host Controller Interface functions (USB 1.x support)
bool uhci_init(void);
bool ohci_init(void);
#endif // MOUSE_H

72
kernel/paging.c Normal file
View File

@@ -0,0 +1,72 @@
#include "paging.h"
#include "io.h"
#include <stdint.h>
#include <stddef.h>
page_directory_entry_t *page_directory = (page_directory_entry_t *)0x100000;
page_table_entry_t *page_table = (page_table_entry_t *)0x101000;
page_table_entry_t *heap_page_table = (page_table_entry_t *)0x102000; // Located right after the page directory
// Helper function to set up the page directory entry
void set_page_directory(page_directory_entry_t *dir) {
for (int i = 0; i < PAGE_DIRECTORY_SIZE; i++) {
dir[i].present = 0;
}
dir[0].present = 1;
dir[0].rw = 1;
dir[0].user = 0;
dir[0].frame = (uint32_t)page_table >> 12;
}
// Helper function to set up the page table entry
void set_page_table(page_table_entry_t *table) {
for (int i = 0; i < PAGE_TABLE_SIZE; i++) {
// Set up page table entries with identity mapping
table[i].present = 1;
table[i].rw = 1; // Read/Write
table[i].user = 0; // Kernel mode
table[i].write_through = 0;
table[i].cache_disabled = 0;
table[i].accessed = 0;
table[i].frame = i; // Identity mapping
}
}
// Enable paging by loading the page directory into CR3 and setting the PG bit in CR0
void enable_paging() {
uint32_t cr0;
// Load page directory into CR3
__asm__("mov %0, %%cr3" : : "r"(page_directory));
// Enable paging (set the PG bit in CR0)
__asm__("mov %%cr0, %0" : "=r"(cr0));
cr0 |= 0x80000000; // Set the PG (paging) bit
__asm__("mov %0, %%cr0" : : "r"(cr0));
}
// Initialize paging: set up the page directory and enable paging
void paging_init() {
// Set up identity-mapped page directory + table
set_page_directory(page_directory);
set_page_table(page_table);
// === Set up heap mapping at 0xC0100000 ===
for (int i = 0; i < PAGE_TABLE_SIZE; i++) {
heap_page_table[i].present = 1;
heap_page_table[i].rw = 1;
heap_page_table[i].user = 0;
heap_page_table[i].write_through = 0;
heap_page_table[i].cache_disabled = 0;
heap_page_table[i].accessed = 0;
heap_page_table[i].frame = (256 + i); // Start physical heap at 1MB (256*4KB = 1MB)
}
// Index 772 = 0xC0100000 / 4MB
page_directory[772].present = 1;
page_directory[772].rw = 1;
page_directory[772].user = 0;
page_directory[772].frame = (uint32_t)heap_page_table >> 12;
enable_paging();
}

49
kernel/paging.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef PAGING_H
#define PAGING_H
#include <stdint.h>
#define PAGE_SIZE 4096 // Page size in bytes
#define PAGE_DIRECTORY_SIZE 1024 // 1024 entries in page directory
#define PAGE_TABLE_SIZE 1024 // 1024 entries in a page table
#define KERNEL_HEAP_START 0xC0100000
// Page Directory and Page Table structure
typedef struct {
uint32_t present : 1; // Present bit (1: page is present in memory)
uint32_t rw : 1; // Read-Write bit (1: page is read-write)
uint32_t user : 1; // User-supervisor bit (1: user mode access)
uint32_t write_through : 1; // Write-through cache
uint32_t cache_disabled : 1; // Cache disabled
uint32_t accessed : 1; // Accessed bit
uint32_t reserved : 1; // Reserved bit
uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB)
uint32_t global : 1; // Global page (can be used across different processes)
uint32_t available : 3; // Available bits for the system
uint32_t frame : 20; // Frame address (physical address)
} __attribute__((packed)) page_table_entry_t;
// Define page directory entry
typedef struct {
uint32_t present : 1;
uint32_t rw : 1;
uint32_t user : 1;
uint32_t write_through : 1;
uint32_t cache_disabled : 1;
uint32_t accessed : 1;
uint32_t reserved : 1;
uint32_t zero : 5; // Must be zero for page directory
uint32_t reserved_2 : 7; // Reserved bits
uint32_t frame : 20; // Frame address of the page table
} __attribute__((packed)) page_directory_entry_t;
extern page_directory_entry_t *page_directory;
extern page_table_entry_t *page_table;
extern page_table_entry_t *heap_page_table;
void paging_init(void);
void set_page_directory(page_directory_entry_t *dir);
void set_page_table(page_table_entry_t *table);
void enable_paging(void);
#endif

19
kernel/panic.c Normal file
View File

@@ -0,0 +1,19 @@
#include "panic.h"
#include "terminal.h"
#include "serial.h"
#include <stdbool.h>
void panic(const char *message) {
terminal_write("KERNEL PANIC: ");
terminal_write(message);
terminal_write("\nSystem halted.\n");
serial_write("KERNEL PANIC: ");
serial_write(message);
serial_write("\nSystem halted.\n");
// Halt the system
while (true) {
__asm__("cli; hlt");
}
}

6
kernel/panic.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef PANIC_H
#define PANIC_H
void panic(const char *message);
#endif // PANIC_H

102
kernel/print.c Normal file
View File

@@ -0,0 +1,102 @@
#include <stdarg.h>
#include "print.h"
#include "serial.h"
#include "terminal.h"
void my_putchar(char ch) {
// Write a single character to standard output
// In a freestanding environment, you might need to implement this differently
// For now, we will use the standard putchar for demonstration
// Replace this with your own implementation if needed
terminal_putchar(ch);
}
void print_string(const char *str) {
// Simple implementation to print a string
while (*str) {
my_putchar(*str++);
}
}
void my_printf(const char *format, ...) {
va_list args;
va_start(args, format);
while (*format) {
if (*format == '%') {
format++; // Move to the next character after '%'
switch (*format) {
case 's': { // String
char *str = va_arg(args, char *);
print_string(str);
break;
}
case 'd': { // Integer
int num = va_arg(args, int);
char buffer[20]; // Buffer to hold the string representation
//TODO: implement `snprintf()`
//snprintf(buffer, sizeof(buffer), "%d", num);
print_string(buffer);
break;
}
case 'c': { // Character
char ch = (char)va_arg(args, int); // Promote char to int
my_putchar(ch);
break;
}
default:
my_putchar('%'); // Print the '%' if no valid format specifier
my_putchar(*format);
break;
}
} else {
my_putchar(*format);
}
format++;
}
va_end(args);
}
void print_hex(uint32_t val, int include_prefix, int suppress_leading_zeros) {
char hex_chars[] = "0123456789ABCDEF";
char buffer[11]; // 8 hex digits + "0x" + null terminator
int pos = 10; // Start from end of buffer (null terminator)
// Null-terminate the buffer
buffer[pos--] = '\0';
// Convert value to hex digits
for (int i = 7; i >= 0; i--) {
int digit = val & 0xF; // Get last 4 bits
buffer[pos--] = hex_chars[digit];
val >>= 4; // Shift right by 4 bits
}
// Add "0x" prefix if requested
if (include_prefix) {
buffer[pos--] = 'x';
buffer[pos--] = '0';
}
// Determine start of output (skip leading zeros if requested)
int start = include_prefix ? 0 : 2; // Start after "0x" if prefix included
if (suppress_leading_zeros && !include_prefix) {
int i = start;
while (i < 9 && buffer[i] == '0') {
i++;
}
if (i == 10) {
// All zeros, output single '0'
terminal_write("0");
serial_write("0");
return;
}
start = i;
}
// Output the result
terminal_write(buffer + start);
serial_write(buffer + start);
}

11
kernel/print.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef PRINT_H
#define PRINT_H
#include "types.h"
void print_string(const char *str);
void my_printf(const char *format, ...);
void print_hex(uint32_t val, int include_prefix, int suppress_leading_zeros);
void my_putchar(char ch);
#endif

62
kernel/scheduler.c Normal file
View File

@@ -0,0 +1,62 @@
#include "scheduler.h"
#include <stddef.h>
static task_t tasks[MAX_TASKS];
static uint32_t task_stacks[MAX_TASKS][STACK_SIZE / sizeof(uint32_t)];
static int task_count = 0;
static task_t *task_list = NULL;
static task_t *current_task = NULL;
void scheduler_init() {
// Initialize task list, etc.
task_list = NULL;
current_task = NULL;
task_count = 0;
}
void scheduler_add_task(void (*entry)(void)) {
if (task_count >= MAX_TASKS || entry == NULL) return;
task_t *new_task = &tasks[task_count];
new_task->id = task_count;
new_task->entry = entry;
// Simulate a stack pointer pointing to the "top" of the stack
new_task->stack_ptr = &task_stacks[task_count][STACK_SIZE / sizeof(uint32_t) - 1];
new_task->next = NULL;
// Add to task list
if (task_list == NULL) {
task_list = new_task;
} else {
task_t *tail = task_list;
while (tail->next) {
tail = tail->next;
}
tail->next = new_task;
}
task_count++;
}
void scheduler_schedule() {
// Very basic round-robin switch
if (current_task && current_task->next) {
current_task = current_task->next;
} else {
current_task = task_list; // Loop back
}
// Call context switch or simulate yielding to current_task
// In real system: context_switch_to(current_task)
if (current_task && current_task->entry) {
current_task->entry(); // Simulate switching by calling
}
}
void scheduler_yield() {
// Stub: manually call schedule for cooperative multitasking
scheduler_schedule();
}

21
kernel/scheduler.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef SCHEDULER_H
#define SCHEDULER_H
#include <stdint.h>
#define MAX_TASKS 8
#define STACK_SIZE 1024
typedef struct task {
uint32_t id;
void (*entry)(void);
uint32_t *stack_ptr;
struct task *next;
} task_t;
void scheduler_init();
void scheduler_add_task(void (*entry)(void));
void scheduler_schedule();
void scheduler_yield(); // Optional for cooperative scheduling
#endif // SCHEDULER_H

40
kernel/serial.c Normal file
View File

@@ -0,0 +1,40 @@
#include "io.h"
#include "serial.h"
#include <stdint.h>
#define COM1 0x3F8
#define COM2 0x2F8
#define COM3 0x3E8
#define COM4 0x2E8
void serial_init_port(uint16_t port) {
outb(port + 1, 0x00);
outb(port + 3, 0x80);
outb(port + 0, 0x03);
outb(port + 1, 0x00);
outb(port + 3, 0x03);
outb(port + 2, 0xC7);
outb(port + 4, 0x0B);
}
void serial_init(void) {
serial_init_port(COM1);
serial_init_port(COM2);
serial_init_port(COM3);
serial_init_port(COM4);
}
void serial_write_char(char c) {
while (!(inb(COM1 + 5) & 0x20));
outb(COM1, c);
}
void serial_write(const char *str) {
while (*str) {
serial_write_char(*str++);
}
}
void serial_write_string(const char* str) {
serial_write(str);
}

12
kernel/serial.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef SERIAL_H
#define SERIAL_H
#include <stdint.h>
void serial_init(void);
void serial_init_port(uint16_t port);
void serial_write_char(char c);
void serial_write(const char *str);
void serial_write_string(const char *str);
#endif

60
kernel/shell.c Normal file
View File

@@ -0,0 +1,60 @@
#include "shell.h"
#include "keyboard.h"
#include "terminal.h"
#include "print.h"
#include "string_utils.h"
void execute(char *input) {
if (my_strcmp(input, "help") == 0) {
my_printf("Available commands: help, clear, exit\n");
} else if (my_strcmp(input, "clear") == 0) {
terminal_clear();
} else {
my_printf("Unknown command: %s\n", input);
}
}
void shell_loop()
{
char input[256];
size_t index = 0;
char c;
while (1)
{
my_printf("> ");
index = 0;
while (1)
{
c = keyboard_get_char(); // Waits for input
if (c == '\n' || c == '\r') // Enter key
{
input[index] = '\0';
my_printf("\n");
break;
}
else if (c == '\b' || c == 127) // Backspace
{
if (index > 0)
{
index--;
my_printf("\b \b"); // Erase last char on screen
}
}
else
{
if (index < sizeof(input) - 1) {
input[index++] = c;
terminal_putchar(c);
}
}
}
if (my_strcmp(input, "exit") == 0)
break;
execute(input);
}
}

7
kernel/shell.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef SHELL_H
#define SHELL_H
void shell_loop(void);
void execute(char *input);
#endif

80
kernel/string_utils.c Normal file
View File

@@ -0,0 +1,80 @@
#include "string_utils.h"
#include <stddef.h>
#include <stdarg.h>
size_t my_strlen(const char *str) {
const char *s = str;
while (*s) s++;
return s - str;
}
// Forward declaration of my_itoa
static size_t my_itoa(int value, char *str, size_t size);
int my_vsnprintf(char *str, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
size_t written = 0; // Change to size_t
for (const char *p = format; *p != '\0' && written < size - 1; p++) {
if (*p == '%') {
p++;
if (*p == 's') {
const char *s = va_arg(args, const char *);
while (*s && written < size - 1) {
str[written++] = *s++;
}
} else if (*p == 'd') {
// Handle integer formatting
written += my_itoa(va_arg(args, int), str + written, size - written);
} else {
// Handle other formats as needed
str[written++] = *p; // Just copy the character
}
} else {
str[written++] = *p;
}
}
str[written] = '\0'; // Null-terminate the string
va_end(args);
return written;
}
static size_t my_itoa(int value, char *str, size_t size) {
size_t written = 0; // Change to size_t
if (value < 0) {
if (written < size - 1) {
str[written++] = '-';
}
value = -value;
}
// Convert integer to string
int temp = value;
int digits = 0;
do {
digits++;
temp /= 10;
} while (temp);
if (written + digits >= size) {
digits = size - written - 1; // Prevent overflow
}
str += written + digits; // Move pointer to the end
*str-- = '\0'; // Null-terminate the string
do {
*str-- = (value % 10) + '0';
value /= 10;
} while (value && str >= str - digits);
return written + digits; // Return total written characters
}
int my_strcmp(const char *str1, const char *str2) {
while (*str1 && (*str1 == *str2)) {
str1++;
str2++;
}
return *(unsigned char *)str1 - *(unsigned char *)str2;
}

11
kernel/string_utils.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef STRING_UTILS_H
#define STRING_UTILS_H
#include <stddef.h>
#include <stdarg.h> // Include for va_list and related macros
size_t my_strlen(const char *str); // Renamed to avoid conflict
int my_vsnprintf(char *str, size_t size, const char *format, ...); // Renamed to avoid conflict
int my_strcmp(const char *str1, const char *str2);
#endif // STRING_UTILS_H

29
kernel/syscalls.c Normal file
View File

@@ -0,0 +1,29 @@
#include "syscalls.h"
#include "scheduler.h"
#include <stdarg.h>
void syscall_handler(int code, va_list args) {
switch (code) {
case SYSCALL_INIT:
scheduler_init();
break;
case SYSCALL_SPAWN: {
void (*entry)(void) = va_arg(args, void (*)(void));
scheduler_add_task(entry);
break;
}
case SYSCALL_YIELD:
scheduler_yield();
break;
default:
// Unknown syscall
break;
}
}
void syscall(int code, ...) {
va_list args;
va_start(args, code);
syscall_handler(code, args);
va_end(args);
}

17
kernel/syscalls.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef SYSCALLS_H
#define SYSCALLS_H
#include <stdarg.h>
// Syscall numbers
typedef enum {
SYSCALL_INIT = 0,
SYSCALL_SPAWN,
SYSCALL_YIELD
} syscall_code_t;
// Syscall dispatcher
void syscall_handler(int code, va_list args);
// Syscall interface
void syscall(int code, ...);
#endif // SYSCALLS_H

106
kernel/terminal.c Normal file
View File

@@ -0,0 +1,106 @@
#include <stdint.h>
#include "io.h"
#include "terminal.h"
#include "vga.h"
#define VGA_ADDRESS 0xB8000
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
#define WHITE_ON_BLACK 0x0F
static uint16_t* const vga_buffer = (uint16_t*) VGA_ADDRESS;
static uint8_t cursor_x = 0;
static uint8_t cursor_y = 0;
static uint8_t current_color = WHITE_ON_BLACK;
void terminal_initialize(void) {
for (uint16_t y = 0; y < VGA_HEIGHT; y++) {
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
const size_t index = y * VGA_WIDTH + x;
vga_buffer[index] = vga_entry(' ', current_color);
}
}
cursor_x = 0;
cursor_y = 0;
update_cursor(); // Optional: good idea to reset position
}
void terminal_putchar(char c) {
// Handle backspace
if (c == '\b') {
if (cursor_x > 0) {
cursor_x--;
} else if (cursor_y > 0) {
cursor_y--;
cursor_x = VGA_WIDTH - 1;
}
vga_buffer[cursor_y * VGA_WIDTH + cursor_x] = vga_entry(' ', current_color);
update_cursor(); // Optional, if you add cursor updating
return;
}
// Handle newline
if (c == '\n') {
cursor_x = 0;
cursor_y++;
} else {
const size_t index = cursor_y * VGA_WIDTH + cursor_x;
vga_buffer[index] = vga_entry(c, current_color);
cursor_x++;
if (cursor_x >= VGA_WIDTH) {
cursor_x = 0;
cursor_y++;
}
}
// Scroll if needed
if (cursor_y >= VGA_HEIGHT) {
for (uint16_t y = 1; y < VGA_HEIGHT; y++) {
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
vga_buffer[(y - 1) * VGA_WIDTH + x] = vga_buffer[y * VGA_WIDTH + x];
}
}
// Clear the last line
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
vga_buffer[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = vga_entry(' ', current_color);
}
cursor_y = VGA_HEIGHT - 1;
}
update_cursor(); // Optional, if you want the hardware cursor to follow
}
void terminal_write(const char* str) {
for (size_t i = 0; str[i] != '\0'; i++) {
terminal_putchar(str[i]);
}
}
void terminal_setcolor(uint8_t color)
{
current_color = color;
}
void terminal_clear(void) {
for (uint16_t y = 0; y < VGA_HEIGHT; y++) {
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
const size_t index = y * VGA_WIDTH + x;
vga_buffer[index] = vga_entry(' ', current_color);
}
}
cursor_x = 0;
cursor_y = 0;
update_cursor();
}
void update_cursor() {
uint16_t pos = cursor_y * VGA_WIDTH + cursor_x;
outb(0x3D4, 0x0F);
outb(0x3D5, (uint8_t)(pos & 0xFF));
outb(0x3D4, 0x0E);
outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF));
}

13
kernel/terminal.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef TERMINAL_H
#define TERMINAL_H
#include <stdint.h>
void terminal_initialize(void);
void terminal_putchar(char c);
void terminal_write(const char *str);
void terminal_setcolor(uint8_t color);
void terminal_clear(void);
void update_cursor(void);
#endif

119
kernel/threading.c Normal file
View File

@@ -0,0 +1,119 @@
#include "malloc.h"
#include "print.h"
#include "threading.h"
#include "types.h"
#include "utils.h"
#include <stdint.h>
#define MAX_THREADS 16 // Maximum number of threads
#define THREAD_STACK_SIZE 8192 // Stack size for each thread
// The thread table stores information about all threads
static Thread thread_table[MAX_THREADS];
static uint32_t current_thread = 0; // Index of the currently running thread
static uint32_t num_threads = 0; // Number of active threads
// A simple mutex spinlock
static volatile int mutex_locked = 0;
// Function declaration for context_switch
void context_switch(Thread *next);
// Initialize the threading system
void thread_init(void) {
memset(thread_table, 0, sizeof(thread_table));
num_threads = 0;
}
// Create a new thread
void thread_create(Thread *thread __attribute__((unused)), void (*start_routine)(void *), void *arg) {
if (num_threads >= MAX_THREADS) {
my_printf("Error: Maximum thread count reached.\n");
return;
}
// Find an empty slot for the new thread
int index = num_threads++;
thread_table[index] = (Thread){0};
// Set up the new thread
thread_table[index].start_routine = start_routine;
thread_table[index].arg = arg;
thread_table[index].stack_size = THREAD_STACK_SIZE;
thread_table[index].stack = (uint32_t*)malloc(THREAD_STACK_SIZE);
thread_table[index].stack_top = thread_table[index].stack + THREAD_STACK_SIZE / sizeof(uint32_t);
// Initialize the stack (simulate pushing the function's return address)
uint32_t *stack_top = thread_table[index].stack_top;
*(--stack_top) = (uint32_t)start_routine; // Return address (the thread's entry point)
*(--stack_top) = (uint32_t)arg; // Argument to pass to the thread
// Set the thread's state to ready
thread_table[index].state = THREAD_READY;
// If this is the first thread, switch to it
if (index == 0) {
scheduler();
}
}
// Yield the CPU to another thread
void thread_yield(void) {
// Find the next thread in a round-robin manner
uint32_t next_thread = (current_thread + 1) % num_threads;
while (next_thread != current_thread && thread_table[next_thread].state != THREAD_READY) {
next_thread = (next_thread + 1) % num_threads;
}
if (next_thread != current_thread) {
current_thread = next_thread;
scheduler();
}
}
// Exit the current thread
void thread_exit(void) {
thread_table[current_thread].state = THREAD_BLOCKED; // Mark the thread as blocked (finished)
free(thread_table[current_thread].stack); // Free the thread's stack
num_threads--; // Decrease thread count
// Yield to the next thread
thread_yield();
}
// Scheduler: This function selects the next thread to run
void scheduler(void) {
// Find the next ready thread
uint32_t next_thread = (current_thread + 1) % num_threads;
while (thread_table[next_thread].state != THREAD_READY) {
next_thread = (next_thread + 1) % num_threads;
}
if (next_thread != current_thread) {
current_thread = next_thread;
context_switch(&thread_table[current_thread]);
}
}
// Context switch to the next thread (assembly would go here to save/load registers)
void context_switch(Thread *next) {
// For simplicity, context switching in this example would involve saving/restoring registers.
// In a real system, you would need to save the CPU state (registers) and restore the next thread's state.
my_printf("Switching to thread...\n");
next->start_routine(next->arg); // Start running the next thread
}
// Simple mutex functions (spinlock)
void mutex_init(void) {
mutex_locked = 0;
}
void mutex_lock(void) {
while (__sync_lock_test_and_set(&mutex_locked, 1)) {
// Busy wait (spinlock)
}
}
void mutex_unlock(void) {
__sync_lock_release(&mutex_locked);
}

36
kernel/threading.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef THREADING_H
#define THREADING_H
#include <stdint.h>
#include <stddef.h>
// Define a basic thread structure (Thread Control Block - TCB)
typedef struct Thread {
void (*start_routine)(void *); // Function pointer to the thread function
void *arg; // Argument to be passed to the thread function
uint32_t *stack; // Pointer to the thread's stack
uint32_t *stack_top; // Top of the stack (where the thread will start)
uint32_t stack_size; // Size of the stack
uint32_t state; // Thread state (running, ready, blocked)
} Thread;
// Thread states
#define THREAD_RUNNING 0
#define THREAD_READY 1
#define THREAD_BLOCKED 2
// Thread management functions
void thread_init(void);
void thread_create(Thread *thread, void (*start_routine)(void *), void *arg);
void thread_yield(void);
void thread_exit(void);
// Scheduler function
void scheduler(void);
// Synchronization functions (mutex spinlocks)
void mutex_init(void);
void mutex_lock(void);
void mutex_unlock(void);
#endif // THREADING_H

36
kernel/timer.c Normal file
View File

@@ -0,0 +1,36 @@
#include "timer.h"
#include "io.h"
#include "isr.h"
#include "terminal.h"
#include "stdio.h"
#include "utils.h"
static volatile uint32_t tick = 0;
void timer_callback(void) {
tick++;
if (tick % 100 == 0) {
char buf[16];
itoa(tick, buf, 10);
terminal_write("Tick count: ");
terminal_write(buf);
terminal_write("\n");
}
outb(0x20, 0x20); // EOI to PIC
}
void timer_init(uint32_t frequency) {
register_interrupt_handler(32, timer_callback); // IRQ0 = Interrupt 32
uint32_t divisor = 1193180 / frequency;
outb(0x43, 0x36); // Command byte
outb(0x40, divisor & 0xFF); // Low byte
outb(0x40, (divisor >> 8)); // High byte
}
uint32_t timer_get_ticks(void) {
return tick;
}

9
kernel/timer.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef TIMER_H
#define TIMER_H
#include <stdint.h>
void timer_init(uint32_t frequency);
uint32_t timer_get_ticks(void);
#endif

1
kernel/types.c Normal file
View File

@@ -0,0 +1 @@
#include "types.h"

62
kernel/types.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef TYPES_H
#define TYPES_H
// ----------------------------
// Fixed-width integer types
// ----------------------------
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsigned short uint16_t;
typedef signed short int16_t;
typedef unsigned int uint32_t;
typedef signed int int32_t;
typedef unsigned long long uint64_t;
typedef signed long long int64_t;
// ----------------------------
// Boolean & NULL definitions
// ----------------------------
#ifndef __cplusplus
typedef enum { false = 0, true = 1 } bool;
#endif
#ifndef NULL
#define NULL ((void*)0)
#endif
// ----------------------------
// OS subsystem types
// ----------------------------
typedef uint32_t size_t;
typedef int32_t ssize_t;
typedef uint32_t phys_addr_t; // Physical address
typedef uint32_t virt_addr_t; // Virtual address
typedef uint32_t pid_t; // Process ID
typedef uint32_t tid_t; // Thread ID
// ----------------------------
// Bitfield & utility macros
// ----------------------------
#define BIT(n) (1U << (n))
#define BITS(m, n) (((1U << ((n) - (m) + 1)) - 1) << (m))
// Align value to next multiple of alignment
#define ALIGN_UP(val, align) (((val) + ((align)-1)) & ~((align)-1))
#define ALIGN_DOWN(val, align) ((val) & ~((align)-1))
// ----------------------------
// Attributes for structures
// ----------------------------
#define PACKED __attribute__((packed))
#define ALIGN(x) __attribute__((aligned(x)))
// ----------------------------
// Likely/unlikely branch hints
// (for future optimization use)
// ----------------------------
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif // TYPES_H

64
kernel/usb.c Normal file
View File

@@ -0,0 +1,64 @@
#include "usb.h"
#include <stdint.h>
#include <stdbool.h>
// USB version detection
bool usb_detect_version(uint16_t *version) {
if (!version) return false;
*version = 0x0110; // Example: USB 1.1
return true;
}
// USB initialization for 1.x
bool usb_init(void) {
// Detect controller type (UHCI or OHCI)
bool uhci_supported = uhci_init();
bool ohci_supported = ohci_init();
if (!uhci_supported && !ohci_supported) {
return false; // No supported controllers found
}
return true;
}
// USB device enumeration (1.x)
bool usb_enumerate_devices(void) {
// Implementation for detecting devices on USB 1.x ports
return true;
}
// HID initialization for USB 1.x
bool usb_hid_init(void) {
// Ensure USB is initialized
if (!usb_init()) return false;
return usb_enumerate_devices();
}
// USB transfers (stubs for 1.x)
bool usb_control_transfer(/* parameters */) {
// Implement control transfer for USB 1.x
return true;
}
bool usb_interrupt_transfer(/* parameters */) {
// Implement interrupt transfer for USB 1.x
return true;
}
bool usb_bulk_transfer(/* parameters */) {
// Implement bulk transfer for USB 1.x
return true;
}
// USB host controller initialization (UHCI & OHCI)
bool uhci_init(void) {
// Initialize UHCI controller (USB 1.x)
return true;
}
bool ohci_init(void) {
// Initialize OHCI controller (USB 1.x)
return true;
}

24
kernel/usb.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef USB_H
#define USB_H
#include <stdint.h>
#include <stdbool.h>
// USB initialization and management functions
bool usb_init(void);
bool usb_detect_version(uint16_t *version);
bool usb_enumerate_devices(void);
// HID-specific functions
bool usb_hid_init(void);
// USB transfer functions
bool usb_control_transfer(/* parameters */);
bool usb_interrupt_transfer(/* parameters */);
bool usb_bulk_transfer(/* parameters */);
// USB host controller initialization (USB 1.x only)
bool uhci_init(void); // UHCI support
bool ohci_init(void); // OHCI support
#endif // USB_H

95
kernel/utils.c Normal file
View File

@@ -0,0 +1,95 @@
#include "utils.h"
static void reverse(char* str, int len) {
int start = 0;
int end = len - 1;
while (start < end) {
char temp = str[start];
str[start++] = str[end];
str[end--] = temp;
}
}
// Integer to ASCII for signed ints
char* itoa(int value, char* str, int base) {
int i = 0;
int isNegative = 0;
unsigned int uval;
if (base < 2 || base > 36) {
str[0] = '\0';
return str;
}
// Handle zero explicitly
if (value == 0) {
str[i++] = '0';
str[i] = '\0';
return str;
}
// Handle negative numbers (only for base 10)
if (value < 0 && base == 10) {
isNegative = 1;
uval = (unsigned int)(-value);
} else {
uval = (unsigned int)value;
}
while (uval != 0) {
int rem = uval % base;
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
uval /= base;
}
if (isNegative) {
str[i++] = '-';
}
str[i] = '\0';
reverse(str, i);
return str;
}
// Integer to ASCII for unsigned ints
char* utoa(unsigned int value, char* str, int base) {
int i = 0;
if (base < 2 || base > 36) {
str[0] = '\0';
return str;
}
if (value == 0) {
str[i++] = '0';
str[i] = '\0';
return str;
}
while (value != 0) {
int rem = value % base;
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
value /= base;
}
str[i] = '\0';
reverse(str, i);
return str;
}
int memcmp(const void *ptr1, const void *ptr2, size_t num) {
const uint8_t *p1 = ptr1, *p2 = ptr2;
for (size_t i = 0; i < num; i++) {
if (p1[i] != p2[i]) {
return p1[i] < p2[i] ? -1 : 1;
}
}
return 0;
}
void *memset(void *dest, int value, size_t len) {
unsigned char *ptr = (unsigned char *)dest;
while (len-- > 0)
*ptr++ = (unsigned char)value;
return dest;
}

15
kernel/utils.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef UTILS_H
#define UTILS_H
#include "types.h"
// Convert integer to string (base is typically 10, 16, etc.)
char* itoa(int value, char* str, int base);
// Convert unsigned integer to string (base is typically 10, 16, etc.)
char* utoa(unsigned int value, char* str, int base);
int memcmp(const void *ptr1, const void *ptr2, size_t num);
void *memset(void *dest, int value, size_t len);
#endif // UTILS_H

151
kernel/vga.c Normal file
View File

@@ -0,0 +1,151 @@
#include "vga.h"
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include "string_utils.h"
void outb(uint16_t port, uint8_t value) {
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
}
// Read a byte from the specified port
uint8_t inb(uint16_t port) {
uint8_t value;
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
uint8_t vga_entry_color(vga_color fg, vga_color bg) {
return fg | bg << 4;
}
uint16_t vga_entry(unsigned char uc, uint8_t color) {
return (uint16_t)uc | (uint16_t)color << 8;
}
void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y) {
const size_t index = y * 80 + x;
((uint16_t*) 0xB8000)[index] = vga_entry(c, color);
}
void vga_clear(uint8_t color) {
uint16_t blank = vga_entry(' ', color);
for (size_t i = 0; i < 80 * 25; ++i) {
((uint16_t*) 0xB8000)[i] = blank;
}
}
void vga_write_string(const char* data, size_t size) {
size_t x = 0;
size_t y = 0;
for (size_t i = 0; i < size; ++i) {
if (x >= 80) { // If we reach the end of a line, move to the next line
x = 0;
y++;
}
if (y >= 25) { // If we reach the bottom of the screen, scroll
vga_scroll();
y = 24; // Reset to the last row
}
vga_put_entry_at(data[i], vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK), x, y);
x++; // Move to the next column
}
}
void vga_scroll(void) {
uint16_t* video_memory = (uint16_t*)0xB8000;
// Shift all lines up by one row (80 columns per row)
for (size_t i = 0; i < 24 * 80; ++i) {
video_memory[i] = video_memory[i + 80]; // Move one row up
}
// Clear the last row (bottom row)
for (size_t i = 24 * 80; i < 25 * 80; ++i) {
video_memory[i] = vga_entry(' ', vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_WHITE));
}
}
void vga_set_cursor_position(size_t x, size_t y) {
uint16_t position = y * 80 + x; // Calculate linear index (y * 80 + x)
// Set the high byte of the cursor position
outb(VGA_PORT_INDEX, 0x0E); // Cursor high byte register
outb(VGA_PORT_DATA, position >> 8);
// Set the low byte of the cursor position
outb(VGA_PORT_INDEX, 0x0F); // Cursor low byte register
outb(VGA_PORT_DATA, position & 0xFF);
}
size_t vga_get_cursor_position(void) {
outb(VGA_PORT_INDEX, 0x0E); // Cursor high byte register
uint8_t high = inb(VGA_PORT_DATA); // Read high byte
outb(VGA_PORT_INDEX, 0x0F); // Cursor low byte register
uint8_t low = inb(VGA_PORT_DATA); // Read low byte
return (high << 8) | low; // Combine the high and low bytes
}
void vga_set_cursor_blinking(bool enable) {
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
uint8_t cursor = inb(VGA_PORT_DATA);
if (enable) {
cursor |= 0x01; // Enable blinking
} else {
cursor &= ~0x01; // Disable blinking
}
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
outb(VGA_PORT_DATA, cursor);
}
void vga_set_cursor_shape(uint8_t start, uint8_t end) {
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
uint8_t cursor = inb(VGA_PORT_DATA);
cursor = (cursor & 0xC0) | (start & 0x3F); // Set start of cursor shape
outb(VGA_PORT_DATA, cursor);
outb(VGA_PORT_INDEX, 0x0B); // Cursor start register
outb(VGA_PORT_DATA, end & 0x3F); // Set end of cursor shape
}
void vga_set_cursor_color(uint8_t color) {
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
uint8_t cursor = inb(VGA_PORT_DATA);
cursor = (cursor & 0xC0) | (color & 0x3F); // Set cursor color
outb(VGA_PORT_DATA, cursor);
}
void vga_set_cursor_blink_rate(uint8_t rate) {
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
uint8_t cursor = inb(VGA_PORT_DATA);
cursor = (cursor & 0xC0) | (rate & 0x3F); // Set cursor blink rate
outb(VGA_PORT_DATA, cursor);
}
void vga_printf(const char* format, ...) {
char buffer[256]; // Buffer to store the formatted string
va_list args;
va_start(args, format);
my_vsnprintf(buffer, sizeof(buffer), format, args); // Use my_vsnprintf instead of vsnprintf
va_end(args);
// Now you can use the buffer with vga_write_string
vga_write_string(buffer, my_strlen(buffer)); // Use my_strlen instead of strlen
}
void vga_init(void) {
// Clear the screen
vga_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY));
// Set the cursor to the top-left corner
vga_set_cursor_position(0, 0);
// Optionally, set cursor blinking and shape
vga_set_cursor_blinking(true);
vga_set_cursor_shape(0x20, 0x3F); // Default shape
}

53
kernel/vga.h Normal file
View File

@@ -0,0 +1,53 @@
#ifndef VGA_H
#define VGA_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdarg.h> // For va_list, va_start, etc.
// VGA color definitions
typedef enum {
VGA_COLOR_BLACK = 0,
VGA_COLOR_BLUE = 1,
VGA_COLOR_GREEN = 2,
VGA_COLOR_CYAN = 3,
VGA_COLOR_RED = 4,
VGA_COLOR_MAGENTA = 5,
VGA_COLOR_BROWN = 6,
VGA_COLOR_LIGHT_GREY = 7,
VGA_COLOR_DARK_GREY = 8,
VGA_COLOR_LIGHT_BLUE = 9,
VGA_COLOR_LIGHT_GREEN = 10,
VGA_COLOR_LIGHT_CYAN = 11,
VGA_COLOR_LIGHT_RED = 12,
VGA_COLOR_LIGHT_MAGENTA = 13,
VGA_COLOR_LIGHT_BROWN = 14,
VGA_COLOR_WHITE = 15,
} vga_color;
// VGA port addresses
typedef enum {
VGA_PORT_INDEX = 0x3D4, // Index register for VGA
VGA_PORT_DATA = 0x3D5 // Data register for VGA
} vga_io_port_t;
// Function prototypes
uint8_t vga_entry_color(vga_color fg, vga_color bg);
uint16_t vga_entry(unsigned char uc, uint8_t color);
void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y);
void vga_clear(uint8_t color);
void vga_write_string(const char* data, size_t size);
void vga_scroll(void);
void vga_set_cursor_position(size_t x, size_t y);
size_t vga_get_cursor_position(void);
void vga_set_cursor_blinking(bool enable);
void vga_set_cursor_shape(uint8_t start, uint8_t end);
void vga_set_cursor_color(uint8_t color);
void vga_set_cursor_blink_rate(uint8_t rate);
void vga_printf(const char* format, ...);
#endif

View File

@@ -1,26 +0,0 @@
ENTRY(boot)
OUTPUT_FORMAT("binary")
SECTIONS {
. = 0x7c00;
.text :
{
*(.boot)
*(.text)
}
.rodata :
{
*(.rodata)
}
.data :
{
*(.data)
}
.bss :
{
*(.bss)
}
}