mirror of
https://github.com/gbowne1/ClassicOS.git
synced 2025-12-07 14:05:25 -08:00
Compare commits
45 Commits
50efcc13fe
...
gbowne1-fi
| Author | SHA1 | Date | |
|---|---|---|---|
| 940b2810cb | |||
| 01f85f97ec | |||
| fd2c567d29 | |||
| 9de9cc6523 | |||
| e9a78c835a | |||
| 77400d8f5a | |||
| cdf5676085 | |||
|
|
8743fa9e24 | ||
| 3036ee3dfd | |||
| d5906d72de | |||
| 2ab0efdee1 | |||
| 0e011c1682 | |||
| eccf9d7d7c | |||
|
|
62fe09d80d | ||
| f1b0670a15 | |||
| 48fdb348ca | |||
| 6dbd08c808 | |||
| 9ac3a2b862 | |||
| 95f0507c24 | |||
| 70539f72b8 | |||
| 1b046776e0 | |||
| 2609f52dd6 | |||
| f2e75c5142 | |||
| 056d3eb374 | |||
| 98f0f58ce4 | |||
| 7d9d0aeee3 | |||
| 8e5dff4271 | |||
| 9aa1b85ca0 | |||
|
|
9216673b18 | ||
| ed07e2cd9c | |||
| a4318d3c79 | |||
| 9cde2e708d | |||
|
|
c22f6b6f14 | ||
| 6267863939 | |||
|
|
49114214cb | ||
| e58abdae1c | |||
|
|
dd37ba8ed6 | ||
| dd68fd805f | |||
| 16309bc306 | |||
| 4a189f482f | |||
| 267130281a | |||
| e1e30b511a | |||
| 109e554524 | |||
| 69762b6650 | |||
| 49361a98be |
69
Makefile
69
Makefile
@@ -1,35 +1,58 @@
|
|||||||
AS = nasm
|
AS = nasm
|
||||||
|
ASFLAGS = -f elf32 -g -F dwarf
|
||||||
CC = gcc
|
CC = gcc
|
||||||
LD = ld
|
LD = ld
|
||||||
QEMU = qemu-system-i386
|
QEMU= qemu-system-i386
|
||||||
IMG_SIZE = 1440k
|
|
||||||
|
|
||||||
BOOT_SRC = bootloader/boot.asm
|
BUILD_DIR = build
|
||||||
BOOT_BIN = build/boot.bin
|
DISK_IMG = $(BUILD_DIR)/disk.img
|
||||||
BOOT_IMG = build/boot.img
|
STAGE2_SIZE = 2048
|
||||||
KERNEL_SRC = kernel/kmain.c
|
|
||||||
KERNEL_BIN = build/kernel.bin
|
|
||||||
DISK_IMG = build/disk.img
|
|
||||||
|
|
||||||
all: $(BOOT_IMG) $(KERNEL_BIN) $(DISK_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))
|
||||||
|
|
||||||
$(BOOT_BIN): $(BOOT_SRC)
|
all: $(DISK_IMG)
|
||||||
$(AS) -f bin -o $@ $<
|
|
||||||
|
|
||||||
$(BOOT_IMG): $(BOOT_BIN)
|
.PHONY: stage1 stage2 kernel run gdb clean
|
||||||
cp $(BOOT_BIN) $@
|
stage1: $(BUILD_DIR)
|
||||||
truncate -s $(IMG_SIZE) $@
|
$(AS) $(ASFLAGS) -o $(BUILD_DIR)/$@.o bootloader/$@.asm
|
||||||
|
$(LD) -Ttext=0x7c00 -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.o
|
||||||
|
objcopy -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin
|
||||||
|
|
||||||
$(KERNEL_BIN): $(KERNEL_SRC)
|
# NOTE: Stage2 final size should be checked against `$(STAGE2_SIZE)` by the build system to avoid an overflow.
|
||||||
$(CC) -ffreestanding -c $< -o build/kernel.o
|
# Alternatively, convey the final stage2 size through other means to stage1.
|
||||||
$(LD) -T bootloader/linker.ld -o $@ build/kernel.o
|
stage2: $(BUILD_DIR)
|
||||||
|
$(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm
|
||||||
|
$(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c
|
||||||
|
$(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o
|
||||||
|
objcopy -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin
|
||||||
|
truncate -s $(STAGE2_SIZE) $(BUILD_DIR)/$@.bin
|
||||||
|
|
||||||
$(DISK_IMG): $(BOOT_IMG) $(KERNEL_BIN)
|
$(BUILD_DIR)/asm_%.o: kernel/%.asm
|
||||||
dd if=$(BOOT_IMG) of=$@ bs=512 seek=4
|
$(AS) $(ASFLAGS) -o $@ $<
|
||||||
dd if=$(KERNEL_BIN) of=$@ bs=512 seek=200
|
|
||||||
|
|
||||||
run: $(DISK_IMG)
|
$(BUILD_DIR)/%.o: kernel/%.c
|
||||||
$(QEMU) -drive file=$<,format=raw,if=floppy
|
$(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $@ $<
|
||||||
|
|
||||||
|
kernel: $(KERNEL_OBJ) | $(BUILD_DIR)
|
||||||
|
$(LD) -melf_i386 -Tkernel/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ)
|
||||||
|
|
||||||
|
$(DISK_IMG): stage1 stage2 kernel
|
||||||
|
dd if=$(BUILD_DIR)/stage1.bin of=$@
|
||||||
|
dd if=$(BUILD_DIR)/stage2.bin of=$@ oflag=append conv=notrunc
|
||||||
|
dd if=$(BUILD_DIR)/kernel.elf of=$@ oflag=append conv=notrunc
|
||||||
|
truncate -s 1M $@
|
||||||
|
|
||||||
|
$(BUILD_DIR):
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
run:
|
||||||
|
qemu-system-i386 -s -S $(DISK_IMG)
|
||||||
|
|
||||||
|
gdb:
|
||||||
|
gdb -x gdb.txt
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build
|
rm -rf $(BUILD_DIR)
|
||||||
|
|||||||
@@ -49,3 +49,6 @@ git clone https://github.com/gbowne1/ClassicOS.git
|
|||||||
cd ClassicOS
|
cd ClassicOS
|
||||||
make
|
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"
|
||||||
|
|||||||
27
bootloader/README.md
Normal file
27
bootloader/README.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# ClassicOS 2-stage bootloader
|
||||||
|
|
||||||
|
Bootloader documentation for ClassicOS
|
||||||
|
|
||||||
|
## Disk image organization:
|
||||||
|
|
||||||
|
```
|
||||||
|
[ 512 B ] [ 2048 B ] [ Unspecified ]
|
||||||
|
Stage 1 Stage 2 Kernel
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stage 1 (`stage1.asm`)
|
||||||
|
|
||||||
|
Responsible for loading the second stage using BIOS routines, and switching to protected mode.
|
||||||
|
|
||||||
|
- Queries CHS parameters from BIOS
|
||||||
|
- Loads the second stage bootloader (2048 B) to `0x7c00`
|
||||||
|
- Sets up a GDT with descriptor entries for code and data both covering the whole 32-bit address space
|
||||||
|
- Enables A20
|
||||||
|
- Set CR0.PE (enable protected mode) and jump to stage 2
|
||||||
|
|
||||||
|
## Stage 2 (`stage2.asm, stage2_load.c`)
|
||||||
|
|
||||||
|
- Set up segment registers
|
||||||
|
- Load the kernel ELF header
|
||||||
|
- Parse the program headers, and load all `PT_LOAD` segments from disk
|
||||||
|
- Jump to the kernel entry
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
[BITS 16]
|
|
||||||
[ORG 0x7C00]
|
|
||||||
|
|
||||||
start:
|
|
||||||
cli
|
|
||||||
xor ax, ax
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov ss, ax
|
|
||||||
mov sp, 0x7C00
|
|
||||||
|
|
||||||
call enable_a20
|
|
||||||
call setup_gdt
|
|
||||||
call switch_to_pm
|
|
||||||
|
|
||||||
; ----------------------
|
|
||||||
; A20 Gate Enable (Fast method)
|
|
||||||
enable_a20:
|
|
||||||
in al, 0x92
|
|
||||||
or al, 0x02
|
|
||||||
out 0x92, al
|
|
||||||
ret
|
|
||||||
|
|
||||||
; ----------------------
|
|
||||||
; Set up a minimal GDT
|
|
||||||
gdt_start:
|
|
||||||
dq 0x0000000000000000 ; null descriptor
|
|
||||||
dq 0x00CF9A000000FFFF ; code segment descriptor
|
|
||||||
dq 0x00CF92000000FFFF ; data segment descriptor
|
|
||||||
|
|
||||||
gdt_descriptor:
|
|
||||||
dw gdt_end - gdt_start - 1 ; size of GDT
|
|
||||||
dd gdt_start ; address of GDT
|
|
||||||
|
|
||||||
gdt_end:
|
|
||||||
|
|
||||||
setup_gdt:
|
|
||||||
lgdt [gdt_descriptor]
|
|
||||||
ret
|
|
||||||
|
|
||||||
; ----------------------
|
|
||||||
; Switch to Protected Mode
|
|
||||||
switch_to_pm:
|
|
||||||
cli
|
|
||||||
mov eax, cr0
|
|
||||||
or eax, 1
|
|
||||||
mov cr0, eax
|
|
||||||
jmp 0x08:protected_mode_entry
|
|
||||||
; ----------------------
|
|
||||||
; 32-bit Protected Mode Entry Point
|
|
||||||
[BITS 32]
|
|
||||||
protected_mode_entry:
|
|
||||||
mov ax, 0x10 ; data segment selector
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov fs, ax
|
|
||||||
mov gs, ax
|
|
||||||
mov ss, ax
|
|
||||||
mov esp, 0x90000
|
|
||||||
|
|
||||||
; Kernel is assumed to be loaded at 0x100000
|
|
||||||
jmp 0x08:0x100000
|
|
||||||
|
|
||||||
; ----------------------
|
|
||||||
; Boot signature
|
|
||||||
times 510 - ($ - $$) db 0
|
|
||||||
dw 0xAA55
|
|
||||||
Binary file not shown.
292
bootloader/stage1.asm
Normal file
292
bootloader/stage1.asm
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
; ==============================================================================
|
||||||
|
; boot.asm - First Stage Bootloader (CHS Based)
|
||||||
|
; ==============================================================================
|
||||||
|
|
||||||
|
; Params for stage2
|
||||||
|
%define s2_addr 1 ; stage2 disk offset, in sectors
|
||||||
|
%define s2_laddr 0x7e00 ; stage2 load address
|
||||||
|
%define s2_size 2048 ; stage2 size
|
||||||
|
%define s2_nsect s2_size / 512 ; stage2 size in sectors
|
||||||
|
|
||||||
|
[BITS 16]
|
||||||
|
global _start
|
||||||
|
|
||||||
|
_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
|
||||||
|
|
||||||
|
; Query bios for disk parameters
|
||||||
|
call get_disk_params
|
||||||
|
|
||||||
|
; 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, s2_nsect ; Number of sectors to read
|
||||||
|
mov bx, 0x7E00 ; Destination address offset ES = 0 (0x0000:0x7E00)
|
||||||
|
call read_chs
|
||||||
|
|
||||||
|
; 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
|
||||||
|
|
||||||
|
get_disk_params:
|
||||||
|
mov ah, 08h ; BIOS: Get Drive Parameters
|
||||||
|
int 13h
|
||||||
|
; TODO: error checking
|
||||||
|
|
||||||
|
; CL bits 0–5 contain sectors per track
|
||||||
|
mov al, cl
|
||||||
|
and al, 3Fh ; mask bits 0–5
|
||||||
|
mov ah, 0
|
||||||
|
mov [sectors_per_track], ax
|
||||||
|
|
||||||
|
; DH = maximum head number (0-based)
|
||||||
|
mov al, dh
|
||||||
|
inc ax ; convert to count (heads = maxhead + 1)
|
||||||
|
mov [heads_per_cylinder], ax
|
||||||
|
ret
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------
|
||||||
|
; CHS Disk Read Routine
|
||||||
|
; AL = number of sectors
|
||||||
|
; CL = starting sector (1-based)
|
||||||
|
; Inputs:
|
||||||
|
; AL = sector count
|
||||||
|
; CH = cylinder
|
||||||
|
; DH = head
|
||||||
|
; CL = sector (1–63, with top 2 bits as high cylinder bits)
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------
|
||||||
|
; 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:
|
||||||
|
; Sector
|
||||||
|
xor dx, dx
|
||||||
|
mov bx, ax
|
||||||
|
div word [sectors_per_track] ; divide lba with max sectors
|
||||||
|
add dl, 1 ; take the remainder, sectors start at 1
|
||||||
|
mov cl, dl ; sector is in cl
|
||||||
|
|
||||||
|
; Head
|
||||||
|
mov ax, bx
|
||||||
|
mov dx, 0
|
||||||
|
div word [sectors_per_track] ; divide lba with max sectors
|
||||||
|
mov dx, 0
|
||||||
|
div word [heads_per_cylinder] ; divide quotient with heads
|
||||||
|
mov dh, dl ; take the remainder, head is in dh
|
||||||
|
|
||||||
|
; Cylinder
|
||||||
|
mov ch, al ; take the quotient, cylinder is in ch
|
||||||
|
ret
|
||||||
|
|
||||||
|
read_chs:
|
||||||
|
pusha
|
||||||
|
push dx
|
||||||
|
|
||||||
|
.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 ; jump to S2
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------
|
||||||
|
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 0
|
||||||
|
heads_per_cylinder dw 0
|
||||||
|
|
||||||
|
times 510 - ($ - $$) db 0
|
||||||
|
dw 0xAA55
|
||||||
98
bootloader/stage2.asm
Normal file
98
bootloader/stage2.asm
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
[BITS 32]
|
||||||
|
global _start
|
||||||
|
global ata_lba_read
|
||||||
|
|
||||||
|
extern load_kernel
|
||||||
|
|
||||||
|
_start:
|
||||||
|
; Set up segments
|
||||||
|
; Data segments
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
mov ss, ax
|
||||||
|
|
||||||
|
; Code segment
|
||||||
|
mov ax, 0x08
|
||||||
|
mov cs, ax
|
||||||
|
|
||||||
|
; Stack (must be identity-mapped)
|
||||||
|
mov esp, 0x90000
|
||||||
|
|
||||||
|
call load_kernel
|
||||||
|
|
||||||
|
jmp eax
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; ATA read sectors (LBA mode)
|
||||||
|
;
|
||||||
|
; sysv32 abi signature:
|
||||||
|
; void ata_lba_read(uint32_t lba, uint8_t nsect, void *addr);
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
ata_lba_read:
|
||||||
|
push ebp
|
||||||
|
mov ebp, esp
|
||||||
|
|
||||||
|
push ebx
|
||||||
|
push ecx
|
||||||
|
push edx
|
||||||
|
push edi
|
||||||
|
|
||||||
|
mov eax, [ebp+8] ; arg #1 = LBA
|
||||||
|
mov cl, [ebp+12] ; arg #2 = # of sectors
|
||||||
|
mov edi, [ebp+16] ; arg #3 = buffer address
|
||||||
|
and eax, 0x0FFFFFFF
|
||||||
|
|
||||||
|
mov ebx, eax ; Save LBA in RBX
|
||||||
|
|
||||||
|
mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA
|
||||||
|
shr eax, 24 ; Get bit 24 - 27 in al
|
||||||
|
or al, 11100000b ; Set bit 6 in al for LBA mode
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
mov edx, 0x01F2 ; Port to send number of sectors
|
||||||
|
mov al, cl ; Get number of sectors from CL
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA
|
||||||
|
mov eax, ebx ; Get LBA from EBX
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA
|
||||||
|
mov eax, ebx ; Get LBA from EBX
|
||||||
|
shr eax, 8 ; Get bit 8 - 15 in AL
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA
|
||||||
|
mov eax, ebx ; Get LBA from EBX
|
||||||
|
shr eax, 16 ; Get bit 16 - 23 in AL
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
mov edx, 0x1F7 ; Command port
|
||||||
|
mov al, 0x20 ; Read with retry.
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
mov bl, cl ; Save # of sectors in BL
|
||||||
|
|
||||||
|
.wait_drq:
|
||||||
|
mov edx, 0x1F7
|
||||||
|
.do_wait_drq:
|
||||||
|
in al, dx
|
||||||
|
test al, 8 ; the sector buffer requires servicing.
|
||||||
|
jz .do_wait_drq ; keep polling until the sector buffer is ready.
|
||||||
|
|
||||||
|
mov edx, 0x1F0 ; Data port, in and out
|
||||||
|
mov ecx, 256
|
||||||
|
rep insw ; in to [RDI]
|
||||||
|
|
||||||
|
dec bl ; are we...
|
||||||
|
jnz .wait_drq ; ...done?
|
||||||
|
|
||||||
|
pop edi
|
||||||
|
pop edx
|
||||||
|
pop ecx
|
||||||
|
pop ebx
|
||||||
|
pop ebp
|
||||||
|
ret
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
ENTRY(start)
|
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
. = 1M;
|
. = 0x7e00;
|
||||||
|
|
||||||
.text : {
|
|
||||||
*(.multiboot)
|
|
||||||
*(.text*)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
.text : { *(.text*) }
|
||||||
.rodata : { *(.rodata*) }
|
.rodata : { *(.rodata*) }
|
||||||
.data : { *(.data*) }
|
.data : { *(.data*) }
|
||||||
|
|
||||||
.bss : {
|
.bss : {
|
||||||
*(.bss*)
|
*(.bss*)
|
||||||
*(COMMON)
|
*(COMMON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
read_buf = .;
|
||||||
}
|
}
|
||||||
118
bootloader/stage2_load.c
Normal file
118
bootloader/stage2_load.c
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Load an ELF executable into memory.
|
||||||
|
static int elf_load(const void* data, void (*load_segment)(uint8_t *vaddr, uint32_t 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;
|
||||||
|
|
||||||
|
uint32_t offset = 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((uint8_t *)vaddr, offset, filesz);
|
||||||
|
load_segment((uint8_t *)vaddr, offset, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define KERN_START_SECT 5
|
||||||
|
#define MAX(a, b) ((a)>(b) ? (a) : (b))
|
||||||
|
|
||||||
|
extern void ata_lba_read(uint32_t lba, uint8_t nsect, void *addr);
|
||||||
|
extern uint8_t read_buf[];
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
total_header_size(const Elf32_Ehdr *header) {
|
||||||
|
uint32_t phend = header->e_phoff + header->e_phentsize*header->e_phnum;
|
||||||
|
|
||||||
|
// Align to 512
|
||||||
|
return (phend + 511) & ~511;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_sectors(uint8_t *vaddr, uint32_t offset, uint32_t size) {
|
||||||
|
// # of sectors to read
|
||||||
|
uint32_t rem_nsect = ((size + 511) & ~511) / 512;
|
||||||
|
|
||||||
|
// Current lba address, offset by the first sector already read
|
||||||
|
uint32_t lba = KERN_START_SECT + offset / 512;
|
||||||
|
|
||||||
|
// Max 255 sectors at a time
|
||||||
|
while (rem_nsect) {
|
||||||
|
uint8_t nsect = rem_nsect > 255 ? 255 : rem_nsect;
|
||||||
|
ata_lba_read(lba, nsect, vaddr);
|
||||||
|
|
||||||
|
vaddr += nsect * 512;
|
||||||
|
rem_nsect -= nsect;
|
||||||
|
lba += nsect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *load_kernel(void) {
|
||||||
|
// Read the first sector
|
||||||
|
ata_lba_read(KERN_START_SECT, 1, read_buf);
|
||||||
|
|
||||||
|
const Elf32_Ehdr* header = (const Elf32_Ehdr*)read_buf;
|
||||||
|
|
||||||
|
// Remaining data size, subtract the first 512B already read
|
||||||
|
uint32_t rem = total_header_size(header) - 512;
|
||||||
|
|
||||||
|
// Read the rest if necessary
|
||||||
|
if (rem)
|
||||||
|
read_sectors(read_buf+512, 512, rem);
|
||||||
|
|
||||||
|
elf_load(read_buf, read_sectors);
|
||||||
|
|
||||||
|
return (void *)header->e_entry;
|
||||||
|
}
|
||||||
6
gdb.txt
Normal file
6
gdb.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
target remote :1234
|
||||||
|
add-symbol-file build/stage1.elf
|
||||||
|
add-symbol-file build/stage2.elf
|
||||||
|
add-symbol-file build/kernel.elf
|
||||||
|
hbreak *0x7c00
|
||||||
|
c
|
||||||
@@ -11,7 +11,7 @@ acpi_rsdp_t* acpi_find_rsdp() {
|
|||||||
acpi_rsdp_t* rsdp = (acpi_rsdp_t*)addr;
|
acpi_rsdp_t* rsdp = (acpi_rsdp_t*)addr;
|
||||||
if (memcmp(rsdp->signature, "RSD PTR ", 8) == 0) {
|
if (memcmp(rsdp->signature, "RSD PTR ", 8) == 0) {
|
||||||
uint8_t checksum = 0;
|
uint8_t checksum = 0;
|
||||||
for (int i = 0; i < sizeof(acpi_rsdp_t); i++) {
|
for (size_t i = 0; i < sizeof(acpi_rsdp_t); i++) { // Change int to size_t
|
||||||
checksum += ((uint8_t*)rsdp)[i];
|
checksum += ((uint8_t*)rsdp)[i];
|
||||||
}
|
}
|
||||||
if (checksum == 0) {
|
if (checksum == 0) {
|
||||||
@@ -36,18 +36,19 @@ acpi_fadt_t* acpi_find_fadt(void* rsdt_or_xsdt) {
|
|||||||
acpi_rsdt_t* rsdt = (acpi_rsdt_t*)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);
|
uint32_t num_tables = (rsdt->length - sizeof(acpi_rsdt_t)) / sizeof(uint32_t);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < num_tables; i++) {
|
for (size_t i = 0; i < num_tables; i++) {
|
||||||
uint32_t table_addr = rsdt->tables[i];
|
uint32_t table_addr = rsdt->tables[i];
|
||||||
acpi_fadt_t* fadt = (acpi_fadt_t*)table_addr;
|
acpi_fadt_t* fadt = (acpi_fadt_t*)table_addr;
|
||||||
if (fadt->signature == 0x50434146) { // "FACP" in ASCII
|
if (fadt->signature == 0x50434146) {
|
||||||
uint8_t checksum = 0;
|
uint8_t checksum = 0;
|
||||||
for (int j = 0; j < fadt->length; j++) {
|
for (size_t j = 0; j < fadt->length; j++) {
|
||||||
checksum += ((uint8_t*)fadt)[j];
|
checksum += ((uint8_t*)fadt)[j];
|
||||||
}
|
}
|
||||||
if (checksum == 0) {
|
if (checksum == 0) {
|
||||||
return fadt; // Valid FADT found
|
return fadt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL; // FADT not found
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
25
kernel/context_switch.s
Normal file
25
kernel/context_switch.s
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
.global ctx_switch
|
||||||
|
|
||||||
|
; void ctx_switch(uint32_t **old_sp_ptr, uint32_t *new_sp);
|
||||||
|
; Arguments on stack (cdecl convention):
|
||||||
|
; [ESP + 4] -> old_sp_ptr (pointer to the 'stack_ptr' field of current task)
|
||||||
|
; [ESP + 8] -> new_sp (value of 'stack_ptr' of the next task)
|
||||||
|
|
||||||
|
ctx_switch:
|
||||||
|
; 1. Save the context of the CURRENT task
|
||||||
|
pushf ; Save EFLAGS (CPU status flags)
|
||||||
|
pusha ; Save all General Purpose Regs (EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI)
|
||||||
|
|
||||||
|
; 2. Save the current stack pointer (ESP) into the pointer passed as 1st arg
|
||||||
|
mov eax, [esp + 40] ; Get 1st argument (old_sp_ptr). Offset 40 = 36 (regs) + 4 (ret addr)
|
||||||
|
mov [eax], esp ; *old_sp_ptr = ESP
|
||||||
|
|
||||||
|
; 3. Load the stack pointer of the NEW task
|
||||||
|
mov esp, [esp + 44] ; Get 2nd argument (new_sp). Offset 44 = 40 + 4
|
||||||
|
|
||||||
|
; 4. Restore the context of the NEW task
|
||||||
|
popa ; Restore all General Purpose Regs
|
||||||
|
popf ; Restore EFLAGS
|
||||||
|
|
||||||
|
; 5. Jump to the new task (The 'ret' pops EIP from the new stack)
|
||||||
|
ret
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "terminal.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) {
|
void cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
|
||||||
asm volatile (
|
__asm__(
|
||||||
"cpuid"
|
"cpuid"
|
||||||
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
|
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
|
||||||
: "a"(function)
|
: "a"(function)
|
||||||
@@ -30,6 +32,6 @@ void identify_cpu() {
|
|||||||
serial_write("\n");
|
serial_write("\n");
|
||||||
|
|
||||||
terminal_write("CPUID max leaf: ");
|
terminal_write("CPUID max leaf: ");
|
||||||
print_hex(eax); // You must implement this (see below)
|
print_hex(eax, false, false); // You must implement this (see below)
|
||||||
terminal_write("\n");
|
terminal_write("\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,45 @@
|
|||||||
#include "debug.h"
|
#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) {
|
void debug_print(const char *str) {
|
||||||
// Implementation for printing debug messages
|
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++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
kernel/display.c
Normal file
36
kernel/display.c
Normal 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
14
kernel/display.h
Normal 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
|
||||||
184
kernel/fat12.c
Normal file
184
kernel/fat12.c
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
#include "fat12.h"
|
||||||
|
#include <stddef.h> // for NULL
|
||||||
|
|
||||||
|
// --- Globals for Filesystem State ---
|
||||||
|
static fat12_bpb_t bpb;
|
||||||
|
static uint32_t fat_start_lba;
|
||||||
|
static uint32_t root_dir_lba;
|
||||||
|
static uint32_t data_start_lba;
|
||||||
|
static uint32_t root_dir_sectors;
|
||||||
|
|
||||||
|
// Scratch buffer to read sectors (avoids large stack usage)
|
||||||
|
static uint8_t g_sector_buffer[FAT12_SECTOR_SIZE];
|
||||||
|
|
||||||
|
// --- Utils (Since we don't have string.h) ---
|
||||||
|
static int k_memcmp(const void *s1, const void *s2, uint32_t n) {
|
||||||
|
const uint8_t *p1 = (const uint8_t *)s1;
|
||||||
|
const uint8_t *p2 = (const uint8_t *)s2;
|
||||||
|
for (uint32_t i = 0; i < n; i++) {
|
||||||
|
if (p1[i] != p2[i]) return p1[i] - p2[i];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts "file.txt" to "FILE TXT" for comparison
|
||||||
|
static void to_fat_name(const char *src, char *dest) {
|
||||||
|
// Initialize with spaces
|
||||||
|
for(int i=0; i<11; i++) dest[i] = ' ';
|
||||||
|
|
||||||
|
int i = 0, j = 0;
|
||||||
|
// Copy Name
|
||||||
|
while (src[i] != '\0' && src[i] != '.' && j < 8) {
|
||||||
|
// Convert to uppercase (simple version)
|
||||||
|
char c = src[i];
|
||||||
|
if (c >= 'a' && c <= 'z') c -= 32;
|
||||||
|
dest[j++] = c;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip extension dot
|
||||||
|
if (src[i] == '.') i++;
|
||||||
|
|
||||||
|
// Copy Extension
|
||||||
|
j = 8;
|
||||||
|
while (src[i] != '\0' && j < 11) {
|
||||||
|
char c = src[i];
|
||||||
|
if (c >= 'a' && c <= 'z') c -= 32;
|
||||||
|
dest[j++] = c;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Core Logic ---
|
||||||
|
|
||||||
|
void fat12_init() {
|
||||||
|
// 1. Read Boot Sector (LBA 0)
|
||||||
|
disk_read_sector(0, g_sector_buffer);
|
||||||
|
|
||||||
|
// 2. Copy BPB data safely
|
||||||
|
// We cast the buffer to our struct
|
||||||
|
fat12_bpb_t *boot_sector = (fat12_bpb_t*)g_sector_buffer;
|
||||||
|
bpb = *boot_sector;
|
||||||
|
|
||||||
|
// 3. Calculate System Offsets
|
||||||
|
fat_start_lba = bpb.reserved_sectors;
|
||||||
|
|
||||||
|
// Root Dir starts after FATs
|
||||||
|
// LBA = Reserved + (FatCount * SectorsPerFat)
|
||||||
|
root_dir_lba = fat_start_lba + (bpb.fat_count * bpb.sectors_per_fat);
|
||||||
|
|
||||||
|
// Calculate size of Root Directory in sectors
|
||||||
|
// (Entries * 32 bytes) / 512
|
||||||
|
root_dir_sectors = (bpb.dir_entries_count * 32 + FAT12_SECTOR_SIZE - 1) / FAT12_SECTOR_SIZE;
|
||||||
|
|
||||||
|
// Data starts after Root Directory
|
||||||
|
data_start_lba = root_dir_lba + root_dir_sectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: Read the FAT table to find the NEXT cluster
|
||||||
|
static uint16_t fat12_get_next_cluster(uint16_t current_cluster) {
|
||||||
|
// FAT12 Offset Calculation:
|
||||||
|
// Offset = Cluster + (Cluster / 2)
|
||||||
|
uint32_t fat_offset = current_cluster + (current_cluster / 2);
|
||||||
|
|
||||||
|
uint32_t fat_sector = fat_start_lba + (fat_offset / FAT12_SECTOR_SIZE);
|
||||||
|
uint32_t ent_offset = fat_offset % FAT12_SECTOR_SIZE;
|
||||||
|
|
||||||
|
// Read the sector containing the FAT entry
|
||||||
|
disk_read_sector(fat_sector, g_sector_buffer);
|
||||||
|
|
||||||
|
// Read 16 bits (2 bytes)
|
||||||
|
// Note: If ent_offset == 511, the entry spans two sectors.
|
||||||
|
// For simplicity in this snippet, we ignore that edge case (rare).
|
||||||
|
// A robust kernel would check if(ent_offset == 511) and read next sector.
|
||||||
|
|
||||||
|
uint16_t val = *(uint16_t*)&g_sector_buffer[ent_offset];
|
||||||
|
|
||||||
|
if (current_cluster & 1) {
|
||||||
|
return val >> 4; // Odd: High 12 bits
|
||||||
|
} else {
|
||||||
|
return val & 0x0FFF; // Even: Low 12 bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_t fat12_open(const char *filename) {
|
||||||
|
file_t file = {0};
|
||||||
|
char target_name[11];
|
||||||
|
to_fat_name(filename, target_name);
|
||||||
|
|
||||||
|
// Search Root Directory
|
||||||
|
for (uint32_t i = 0; i < root_dir_sectors; i++) {
|
||||||
|
disk_read_sector(root_dir_lba + i, g_sector_buffer);
|
||||||
|
|
||||||
|
fat12_entry_t *entry = (fat12_entry_t*)g_sector_buffer;
|
||||||
|
|
||||||
|
// Check all 16 entries in this sector (512 / 32 = 16)
|
||||||
|
for (int j = 0; j < 16; j++) {
|
||||||
|
if (entry[j].filename[0] == 0x00) return file; // End of Dir
|
||||||
|
|
||||||
|
// Check if filename matches
|
||||||
|
if (k_memcmp(entry[j].filename, target_name, 11) == 0) {
|
||||||
|
// Found it!
|
||||||
|
file.start_cluster = entry[j].low_cluster_num;
|
||||||
|
file.size = entry[j].file_size;
|
||||||
|
|
||||||
|
// Initialize file cursor
|
||||||
|
file.current_cluster = file.start_cluster;
|
||||||
|
file.bytes_read = 0;
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not found (file.start_cluster will be 0)
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t fat12_read(file_t *file, uint8_t *buffer, uint32_t bytes_to_read) {
|
||||||
|
if (file->start_cluster == 0) return 0; // File not open
|
||||||
|
|
||||||
|
uint32_t total_read = 0;
|
||||||
|
|
||||||
|
while (bytes_to_read > 0) {
|
||||||
|
// Check for EOF marker in FAT12 (>= 0xFF8)
|
||||||
|
if (file->current_cluster >= 0xFF8) break;
|
||||||
|
|
||||||
|
// Calculate Physical LBA of current cluster
|
||||||
|
// LBA = DataStart + ((Cluster - 2) * SectorsPerCluster)
|
||||||
|
uint32_t lba = data_start_lba + ((file->current_cluster - 2) * bpb.sectors_per_cluster);
|
||||||
|
|
||||||
|
// Read the cluster
|
||||||
|
// NOTE: Assumes SectorsPerCluster = 1 (Standard Floppy)
|
||||||
|
disk_read_sector(lba, g_sector_buffer);
|
||||||
|
|
||||||
|
// Determine how much to copy from this sector
|
||||||
|
uint32_t chunk_size = FAT12_SECTOR_SIZE;
|
||||||
|
|
||||||
|
// If the file is smaller than a sector, or we are at the end
|
||||||
|
if (chunk_size > bytes_to_read) chunk_size = bytes_to_read;
|
||||||
|
|
||||||
|
// Check if we are reading past file size
|
||||||
|
if (file->bytes_read + chunk_size > file->size) {
|
||||||
|
chunk_size = file->size - file->bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy to user buffer
|
||||||
|
for (uint32_t i = 0; i < chunk_size; i++) {
|
||||||
|
buffer[total_read + i] = g_sector_buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
total_read += chunk_size;
|
||||||
|
file->bytes_read += chunk_size;
|
||||||
|
bytes_to_read -= chunk_size;
|
||||||
|
|
||||||
|
// If we finished this cluster, move to the next one
|
||||||
|
if (chunk_size == FAT12_SECTOR_SIZE) { // Or strictly logic based on position
|
||||||
|
file->current_cluster = fat12_get_next_cluster(file->current_cluster);
|
||||||
|
} else {
|
||||||
|
// We finished the file or the request
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total_read;
|
||||||
|
}
|
||||||
67
kernel/fat12.h
Normal file
67
kernel/fat12.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#ifndef FAT12_H
|
||||||
|
#define FAT12_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// --- Configuration ---
|
||||||
|
#define FAT12_SECTOR_SIZE 512
|
||||||
|
|
||||||
|
// --- On-Disk Structures (Must be Packed) ---
|
||||||
|
|
||||||
|
// BIOS Parameter Block (Start of Boot Sector)
|
||||||
|
typedef struct {
|
||||||
|
uint8_t jump[3];
|
||||||
|
char oem[8];
|
||||||
|
uint16_t bytes_per_sector; // 512
|
||||||
|
uint8_t sectors_per_cluster; // 1
|
||||||
|
uint16_t reserved_sectors; // 1 (Boot sector)
|
||||||
|
uint8_t fat_count; // 2
|
||||||
|
uint16_t dir_entries_count; // 224
|
||||||
|
uint16_t total_sectors; // 2880
|
||||||
|
uint8_t media_descriptor; // 0xF0
|
||||||
|
uint16_t sectors_per_fat; // 9
|
||||||
|
uint16_t sectors_per_track; // 18
|
||||||
|
uint16_t heads; // 2
|
||||||
|
uint32_t hidden_sectors;
|
||||||
|
uint32_t total_sectors_large;
|
||||||
|
} __attribute__((packed)) fat12_bpb_t;
|
||||||
|
|
||||||
|
// Directory Entry (32 bytes)
|
||||||
|
typedef struct {
|
||||||
|
char filename[8];
|
||||||
|
char ext[3];
|
||||||
|
uint8_t attributes;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t creation_ms;
|
||||||
|
uint16_t creation_time;
|
||||||
|
uint16_t creation_date;
|
||||||
|
uint16_t last_access_date;
|
||||||
|
uint16_t high_cluster_num; // Always 0 in FAT12
|
||||||
|
uint16_t last_mod_time;
|
||||||
|
uint16_t last_mod_date;
|
||||||
|
uint16_t low_cluster_num; // The starting cluster
|
||||||
|
uint32_t file_size; // Size in bytes
|
||||||
|
} __attribute__((packed)) fat12_entry_t;
|
||||||
|
|
||||||
|
// --- Kernel File Handle ---
|
||||||
|
// This is what your kernel uses to track an open file
|
||||||
|
typedef struct {
|
||||||
|
char name[11];
|
||||||
|
uint32_t size;
|
||||||
|
uint16_t start_cluster;
|
||||||
|
uint16_t current_cluster;
|
||||||
|
uint32_t current_sector_in_cluster;
|
||||||
|
uint32_t bytes_read;
|
||||||
|
} file_t;
|
||||||
|
|
||||||
|
// --- Public API ---
|
||||||
|
|
||||||
|
// You must implement this in your disk driver (e.g., floppy.c)
|
||||||
|
// Returns 0 on success, non-zero on error.
|
||||||
|
extern int disk_read_sector(uint32_t lba, uint8_t *buffer);
|
||||||
|
|
||||||
|
void fat12_init();
|
||||||
|
file_t fat12_open(const char *filename);
|
||||||
|
uint32_t fat12_read(file_t *file, uint8_t *buffer, uint32_t bytes_to_read);
|
||||||
|
|
||||||
|
#endif // FAT12_H
|
||||||
92
kernel/framebuffer.c
Normal file
92
kernel/framebuffer.c
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#include "framebuffer.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Simple init
|
||||||
|
void framebuffer_init(framebuffer_t *fb, void *base, uint32_t width, uint32_t height, uint32_t pitch, uint8_t bpp) {
|
||||||
|
fb->base = base;
|
||||||
|
fb->width = width;
|
||||||
|
fb->height = height;
|
||||||
|
fb->pitch = pitch;
|
||||||
|
fb->bpp = bpp;
|
||||||
|
fb->initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack color into 32-bit value. Format: 0xAARRGGBB
|
||||||
|
uint32_t framebuffer_pack_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||||
|
return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void framebuffer_put_pixel(framebuffer_t *fb, uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||||
|
if (!fb->initialized) return;
|
||||||
|
if (x >= fb->width || y >= fb->height) return;
|
||||||
|
if (fb->bpp != 32) return; // only 32bpp implemented here
|
||||||
|
|
||||||
|
uint8_t *line = (uint8_t*)fb->base + (size_t)y * fb->pitch;
|
||||||
|
uint32_t *pixel = (uint32_t*)(line + x * 4);
|
||||||
|
*pixel = framebuffer_pack_color(r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void framebuffer_clear(framebuffer_t *fb, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
if (!fb->initialized) return;
|
||||||
|
if (fb->bpp != 32) return;
|
||||||
|
|
||||||
|
uint32_t color = framebuffer_pack_color(r,g,b,0xFF);
|
||||||
|
for (uint32_t y = 0; y < fb->height; ++y) {
|
||||||
|
uint32_t *row = (uint32_t*)((uint8_t*)fb->base + (size_t)y * fb->pitch);
|
||||||
|
for (uint32_t x = 0; x < fb->width; ++x) {
|
||||||
|
row[x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void framebuffer_fill_rect(framebuffer_t *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
if (!fb->initialized) return;
|
||||||
|
if (fb->bpp != 32) return;
|
||||||
|
|
||||||
|
if (x >= fb->width || y >= fb->height) return;
|
||||||
|
if (x + w > fb->width) w = fb->width - x;
|
||||||
|
if (y + h > fb->height) h = fb->height - y;
|
||||||
|
|
||||||
|
uint32_t color = framebuffer_pack_color(r,g,b,0xFF);
|
||||||
|
for (uint32_t yy = 0; yy < h; ++yy) {
|
||||||
|
uint32_t *row = (uint32_t*)((uint8_t*)fb->base + (size_t)(y + yy) * fb->pitch) + x;
|
||||||
|
for (uint32_t xx = 0; xx < w; ++xx) {
|
||||||
|
row[xx] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple blit from a source buffer with 32bpp pixels and given pitch (bytes per line)
|
||||||
|
void framebuffer_blit(framebuffer_t *fb, uint32_t dst_x, uint32_t dst_y, const void *src, uint32_t src_w, uint32_t src_h, uint32_t src_pitch) {
|
||||||
|
if (!fb->initialized) return;
|
||||||
|
if (fb->bpp != 32) return;
|
||||||
|
|
||||||
|
if (dst_x >= fb->width || dst_y >= fb->height) return;
|
||||||
|
|
||||||
|
uint32_t copy_w = src_w;
|
||||||
|
uint32_t copy_h = src_h;
|
||||||
|
if (dst_x + copy_w > fb->width) copy_w = fb->width - dst_x;
|
||||||
|
if (dst_y + copy_h > fb->height) copy_h = fb->height - dst_y;
|
||||||
|
|
||||||
|
const uint8_t *s = (const uint8_t*)src;
|
||||||
|
for (uint32_t yy = 0; yy < copy_h; ++yy) {
|
||||||
|
uint32_t *dst_row = (uint32_t*)((uint8_t*)fb->base + (size_t)(dst_y + yy) * fb->pitch) + dst_x;
|
||||||
|
const uint32_t *src_row = (const uint32_t*)(s + (size_t)yy * src_pitch);
|
||||||
|
for (uint32_t xx = 0; xx < copy_w; ++xx) {
|
||||||
|
dst_row[xx] = src_row[xx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void framebuffer_test_pattern(framebuffer_t *fb) {
|
||||||
|
if (!fb->initialized) return;
|
||||||
|
// simple color bars
|
||||||
|
uint32_t band_h = fb->height / 6;
|
||||||
|
framebuffer_fill_rect(fb, 0, 0, fb->width, band_h, 0xFF, 0x00, 0x00); // red
|
||||||
|
framebuffer_fill_rect(fb, 0, band_h, fb->width, band_h, 0x00, 0xFF, 0x00); // green
|
||||||
|
framebuffer_fill_rect(fb, 0, band_h*2, fb->width, band_h, 0x00, 0x00, 0xFF); // blue
|
||||||
|
framebuffer_fill_rect(fb, 0, band_h*3, fb->width, band_h, 0xFF, 0xFF, 0x00); // yellow
|
||||||
|
framebuffer_fill_rect(fb, 0, band_h*4, fb->width, band_h, 0xFF, 0x00, 0xFF); // magenta
|
||||||
|
framebuffer_fill_rect(fb, 0, band_h*5, fb->width, fb->height - band_h*5, 0x00, 0xFF, 0xFF); // cyan
|
||||||
|
}
|
||||||
25
kernel/framebuffer.h
Normal file
25
kernel/framebuffer.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef FRAMEBUFFER_H
|
||||||
|
#define FRAMEBUFFER_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *base;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t pitch;
|
||||||
|
uint8_t bpp;
|
||||||
|
bool initialized;
|
||||||
|
} framebuffer_t;
|
||||||
|
|
||||||
|
void framebuffer_init(framebuffer_t *fb, void *base, uint32_t width, uint32_t height, uint32_t pitch, uint8_t bpp);
|
||||||
|
uint32_t framebuffer_pack_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||||
|
void framebuffer_put_pixel(framebuffer_t *fb, uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||||
|
void framebuffer_clear(framebuffer_t *fb, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void framebuffer_fill_rect(framebuffer_t *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void framebuffer_blit(framebuffer_t *fb, uint32_t dst_x, uint32_t dst_y, const void *src, uint32_t src_w, uint32_t src_h, uint32_t src_pitch);
|
||||||
|
void framebuffer_test_pattern(framebuffer_t *fb);
|
||||||
|
|
||||||
|
#endif /* FRAMEBUFFER_H */
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#include "fs.h"
|
|
||||||
|
|
||||||
void fs_init() {
|
|
||||||
// Filesystem initialization code
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef FS_H
|
|
||||||
#define FS_H
|
|
||||||
|
|
||||||
void fs_init();
|
|
||||||
|
|
||||||
#endif // FS_H
|
|
||||||
@@ -52,7 +52,7 @@ void idt_set_gate(int n, uint32_t handler) {
|
|||||||
|
|
||||||
// Load IDT via lidt
|
// Load IDT via lidt
|
||||||
static void idt_load() {
|
static void idt_load() {
|
||||||
asm volatile("lidt (%0)" : : "r" (&idt_ptr));
|
__asm__("lidt (%0)" : : "r" (&idt_ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// IDT initialization
|
// IDT initialization
|
||||||
|
|||||||
26
kernel/io.h
26
kernel/io.h
@@ -1,13 +1,35 @@
|
|||||||
#ifndef IO_H
|
#ifndef IO_H
|
||||||
#define IO_H
|
#define IO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
static inline void outb(uint16_t port, uint8_t val) {
|
static inline void outb(uint16_t port, uint8_t val) {
|
||||||
asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
|
__asm__("outb %0, %1" : : "a"(val), "Nd"(port));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint8_t inb(uint16_t port) {
|
static inline uint8_t inb(uint16_t port) {
|
||||||
uint8_t ret;
|
uint8_t ret;
|
||||||
asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
__asm__("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outw(uint16_t port, uint16_t val) {
|
||||||
|
__asm__("outw %0, %1" : : "a"(val), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t inw(uint16_t port) {
|
||||||
|
uint16_t ret;
|
||||||
|
__asm__("inw %1, %0" : "=a"(ret) : "Nd"(port));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outl(uint16_t port, uint32_t val) {
|
||||||
|
__asm__("outl %0, %1" : : "a"(val), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t inl(uint16_t port) {
|
||||||
|
uint32_t ret;
|
||||||
|
__asm__("inl %1, %0" : "=a"(ret) : "Nd"(port));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
75
kernel/irq.c
75
kernel/irq.c
@@ -1,5 +1,76 @@
|
|||||||
|
#include "idt.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "isr.h"
|
||||||
|
|
||||||
void irq_init() {
|
#define PIC1_CMD 0x20
|
||||||
// IRQ initialization code
|
#define PIC1_DATA 0x21
|
||||||
|
#define PIC2_CMD 0xA0
|
||||||
|
#define PIC2_DATA 0xA1
|
||||||
|
|
||||||
|
// FIXME: stubs
|
||||||
|
void irq0() {}
|
||||||
|
void irq1() {}
|
||||||
|
void irq2() {}
|
||||||
|
void irq3() {}
|
||||||
|
void irq4() {}
|
||||||
|
void irq5() {}
|
||||||
|
void irq6() {}
|
||||||
|
void irq7() {}
|
||||||
|
void irq8() {}
|
||||||
|
void irq9() {}
|
||||||
|
void irq10() {}
|
||||||
|
void irq11() {}
|
||||||
|
void irq12() {}
|
||||||
|
void irq13() {}
|
||||||
|
void irq14() {}
|
||||||
|
void irq15() {}
|
||||||
|
// --- stubs end
|
||||||
|
|
||||||
|
void irq_remap(void)
|
||||||
|
{
|
||||||
|
outb(PIC1_CMD, 0x11); // ICW1 – edge triggered, cascade, need ICW4
|
||||||
|
outb(PIC2_CMD, 0x11);
|
||||||
|
|
||||||
|
outb(PIC1_DATA, 0x20); // ICW2 – master base vector
|
||||||
|
outb(PIC2_DATA, 0x28); // ICW2 – slave base vector
|
||||||
|
|
||||||
|
outb(PIC1_DATA, 0x04); // ICW3 – slave on IRQ2
|
||||||
|
outb(PIC2_DATA, 0x02); // ICW3 – cascade identity
|
||||||
|
|
||||||
|
outb(PIC1_DATA, 0x01); // ICW4 – 8086 mode
|
||||||
|
outb(PIC2_DATA, 0x01);
|
||||||
|
|
||||||
|
// Mask everything except IRQ0 (timer) and IRQ1 (keyboard) for now
|
||||||
|
outb(PIC1_DATA, 0b11111001);
|
||||||
|
outb(PIC2_DATA, 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_install(void)
|
||||||
|
{
|
||||||
|
irq_remap();
|
||||||
|
|
||||||
|
/* Fill IRQ entries in the IDT (0x20 … 0x2F) */
|
||||||
|
//extern void irq0(), irq1(), irq2(), irq3(), irq4(), irq5(), irq6(), irq7();
|
||||||
|
//extern void irq8(), irq9(), irq10(), irq11(), irq12(), irq13(), irq14(), irq15();
|
||||||
|
|
||||||
|
idt_set_gate(0x20, (uint32_t)irq0);
|
||||||
|
idt_set_gate(0x21, (uint32_t)irq1);
|
||||||
|
/* … repeat for the rest or loop … */
|
||||||
|
for (int i = 2; i < 16; ++i)
|
||||||
|
idt_set_gate(0x20 + i, (uint32_t)irq0 + i * 8); // crude but works
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called from the assembly stubs (see irq.asm below) */
|
||||||
|
void irq_handler(uint32_t int_num)
|
||||||
|
{
|
||||||
|
/* int_num is the *remapped* vector, e.g. 0x21 for keyboard */
|
||||||
|
if (interrupt_handlers[int_num]) {
|
||||||
|
interrupt_handlers[int_num]();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- EOI ---- */
|
||||||
|
if (int_num >= 0x28) // slave PIC
|
||||||
|
outb(PIC2_CMD, 0x20);
|
||||||
|
outb(PIC1_CMD, 0x20); // always master
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#ifndef IRQ_H
|
#ifndef IRQ_H
|
||||||
#define IRQ_H
|
#define IRQ_H
|
||||||
|
|
||||||
void irq_init();
|
#include "types.h"
|
||||||
|
|
||||||
#endif // IRQ_H
|
void irq_remap(void);
|
||||||
|
void irq_install(void);
|
||||||
|
void irq_handler(uint32_t int_num);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[BITS 32]
|
[BITS 32]
|
||||||
[GLOBAL isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8, isr9]
|
GLOBAL isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8, isr9
|
||||||
[GLOBAL isr10, isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19]
|
GLOBAL isr10, isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19
|
||||||
[GLOBAL isr20, isr21, isr22, isr23, isr24, isr25, isr26, isr27, isr28, isr29]
|
GLOBAL isr20, isr21, isr22, isr23, isr24, isr25, isr26, isr27, isr28, isr29
|
||||||
[GLOBAL isr30, isr31, isr_default]
|
GLOBAL isr30, isr31, isr_default
|
||||||
|
|
||||||
[EXTERN isr_handler]
|
[EXTERN isr_handler]
|
||||||
|
|
||||||
|
|||||||
13
kernel/isr.c
13
kernel/isr.c
@@ -2,13 +2,22 @@
|
|||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "isr.h"
|
#include "isr.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
#include "print.h"
|
||||||
|
|
||||||
static isr_callback_t interrupt_handlers[MAX_INTERRUPTS] = { 0 };
|
isr_callback_t interrupt_handlers[MAX_INTERRUPTS] = { 0 };
|
||||||
|
|
||||||
void isr_handler(uint32_t int_num, uint32_t err_code) {
|
void isr_handler(uint32_t int_num, uint32_t err_code) {
|
||||||
terminal_write("Interrupt occurred: ");
|
terminal_write("Interrupt occurred: ");
|
||||||
|
|
||||||
|
print_hex(int_num, true, false);
|
||||||
|
terminal_write("\n");
|
||||||
|
|
||||||
serial_write("INT triggered\n");
|
serial_write("INT triggered\n");
|
||||||
|
|
||||||
|
terminal_write("Error code: ");
|
||||||
|
print_hex(err_code, true, false);
|
||||||
|
terminal_write("\n");
|
||||||
|
|
||||||
if (interrupt_handlers[int_num]) {
|
if (interrupt_handlers[int_num]) {
|
||||||
interrupt_handlers[int_num](); // Call registered handler
|
interrupt_handlers[int_num](); // Call registered handler
|
||||||
} else {
|
} else {
|
||||||
@@ -24,7 +33,7 @@ void isr_handler(uint32_t int_num, uint32_t err_code) {
|
|||||||
|
|
||||||
// Halt CPU
|
// Halt CPU
|
||||||
while (1) {
|
while (1) {
|
||||||
asm volatile ("hlt");
|
__asm__("hlt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#define MAX_INTERRUPTS 256
|
#define MAX_INTERRUPTS 256
|
||||||
|
|
||||||
typedef void (*isr_callback_t)(void);
|
typedef void (*isr_callback_t)(void);
|
||||||
|
extern isr_callback_t interrupt_handlers[MAX_INTERRUPTS];
|
||||||
|
|
||||||
void isr_handler(uint32_t int_num, uint32_t err_code);
|
void isr_handler(uint32_t int_num, uint32_t err_code);
|
||||||
void register_interrupt_handler(uint8_t n, isr_callback_t handler);
|
void register_interrupt_handler(uint8_t n, isr_callback_t handler);
|
||||||
|
|||||||
@@ -4,8 +4,12 @@
|
|||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
|
||||||
#define KEYBOARD_DATA_PORT 0x60
|
#define KEYBOARD_DATA_PORT 0x60
|
||||||
|
#define KEY_BUFFER_SIZE 256
|
||||||
|
|
||||||
static char key_buffer[256];
|
static char key_buffer[KEY_BUFFER_SIZE];
|
||||||
|
static uint8_t buffer_head = 0; // Write position (interrupt)
|
||||||
|
static uint8_t buffer_tail = 0; // Read position (get_char)
|
||||||
|
static uint8_t buffer_count = 0;
|
||||||
static uint8_t buffer_index = 0;
|
static uint8_t buffer_index = 0;
|
||||||
|
|
||||||
// Basic US QWERTY keymap (scancode to ASCII)
|
// Basic US QWERTY keymap (scancode to ASCII)
|
||||||
@@ -20,18 +24,24 @@ static const char scancode_map[128] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Interrupt handler for IRQ1
|
// Interrupt handler for IRQ1
|
||||||
void keyboard_callback() {
|
void keyboard_callback(void) {
|
||||||
uint8_t scancode = inb(KEYBOARD_DATA_PORT);
|
uint8_t scancode = inb(KEYBOARD_DATA_PORT);
|
||||||
|
|
||||||
// Ignore key releases (scancodes with high bit set)
|
if (scancode & 0x80) return; // Ignore key release
|
||||||
if (scancode & 0x80)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char c = scancode_map[scancode];
|
char c = scancode_map[scancode];
|
||||||
if (c && buffer_index < sizeof(key_buffer)) {
|
if (!c) return;
|
||||||
key_buffer[buffer_index++] = c;
|
|
||||||
terminal_putchar(c); // Echo to terminal (optional)
|
uint8_t next_head = (buffer_head + 1) % KEY_BUFFER_SIZE;
|
||||||
}
|
|
||||||
|
// Drop key if buffer full
|
||||||
|
if (next_head == buffer_tail) return;
|
||||||
|
|
||||||
|
key_buffer[buffer_head] = c;
|
||||||
|
buffer_head = next_head;
|
||||||
|
buffer_count++;
|
||||||
|
|
||||||
|
terminal_putchar(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_init() {
|
void keyboard_init() {
|
||||||
@@ -39,15 +49,17 @@ void keyboard_init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Blocking read (returns one char)
|
// Blocking read (returns one char)
|
||||||
char keyboard_get_char() {
|
char keyboard_get_char(void) {
|
||||||
while (buffer_index == 0);
|
while (buffer_count == 0) {
|
||||||
char c = key_buffer[0];
|
__asm__ __volatile__("hlt"); // Better than busy loop
|
||||||
|
|
||||||
// Shift buffer left
|
|
||||||
for (uint8_t i = 1; i < buffer_index; i++) {
|
|
||||||
key_buffer[i - 1] = key_buffer[i];
|
|
||||||
}
|
}
|
||||||
buffer_index--;
|
|
||||||
|
char c;
|
||||||
|
__asm__ __volatile__("cli");
|
||||||
|
c = key_buffer[buffer_tail];
|
||||||
|
buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE;
|
||||||
|
buffer_count--;
|
||||||
|
__asm__ __volatile__("sti");
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
@@ -8,6 +7,12 @@
|
|||||||
#include "memmap.h"
|
#include "memmap.h"
|
||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
#include "kmalloc.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "keyboard.h"
|
||||||
|
#include "irq.h"
|
||||||
|
|
||||||
#define LPT1 0x378
|
#define LPT1 0x378
|
||||||
|
|
||||||
@@ -37,6 +42,9 @@ void kmain(void) {
|
|||||||
idt_init();
|
idt_init();
|
||||||
serial_write("IDT initialized.\n");
|
serial_write("IDT initialized.\n");
|
||||||
|
|
||||||
|
irq_install();
|
||||||
|
__asm__ __volatile__ ("sti");
|
||||||
|
|
||||||
terminal_write("Enabling paging...\n");
|
terminal_write("Enabling paging...\n");
|
||||||
paging_init();
|
paging_init();
|
||||||
serial_write("Paging initialized.\n");
|
serial_write("Paging initialized.\n");
|
||||||
@@ -45,13 +53,16 @@ void kmain(void) {
|
|||||||
kmalloc_init(0xC0100000); // Virtual heap start address (must be mapped!)
|
kmalloc_init(0xC0100000); // Virtual heap start address (must be mapped!)
|
||||||
serial_write("kmalloc initialized.\n");
|
serial_write("kmalloc initialized.\n");
|
||||||
|
|
||||||
void* ptr = kmalloc(128); // Allocation test
|
|
||||||
serial_write("Allocated 128 bytes.\n");
|
serial_write("Allocated 128 bytes.\n");
|
||||||
|
|
||||||
terminal_write("Initializing timer...\n");
|
terminal_write("Initializing timer...\n");
|
||||||
timer_init(100); // 100 Hz (10 ms interval)
|
timer_init(100); // 100 Hz (10 ms interval)
|
||||||
serial_write("Timer initialized.\n");
|
serial_write("Timer initialized.\n");
|
||||||
|
|
||||||
|
terminal_write("Initializing keyboard...\n");
|
||||||
|
keyboard_init();
|
||||||
|
serial_write("Keyboard initialized.\n");
|
||||||
|
|
||||||
terminal_write("Getting memory map...\n");
|
terminal_write("Getting memory map...\n");
|
||||||
memory_map_entry_t mmap[32];
|
memory_map_entry_t mmap[32];
|
||||||
uint32_t mmap_size = get_memory_map(mmap, 32);
|
uint32_t mmap_size = get_memory_map(mmap, 32);
|
||||||
@@ -62,9 +73,9 @@ void kmain(void) {
|
|||||||
char buf[32];
|
char buf[32];
|
||||||
for (uint32_t i = 0; i < mmap_size; i++) {
|
for (uint32_t i = 0; i < mmap_size; i++) {
|
||||||
terminal_write(" - Base: ");
|
terminal_write(" - Base: ");
|
||||||
print_hex((uint32_t)(mmap[i].base_addr & 0xFFFFFFFF)); // Lower 32 bits
|
print_hex((uint32_t)(mmap[i].base_addr & 0xFFFFFFFF), true, false); // Lower 32 bits
|
||||||
terminal_write(", Length: ");
|
terminal_write(", Length: ");
|
||||||
print_hex((uint32_t)(mmap[i].length & 0xFFFFFFFF)); // Lower 32 bits
|
print_hex((uint32_t)(mmap[i].length & 0xFFFFFFFF), true, false); // Lower 32 bits
|
||||||
terminal_write(", Type: ");
|
terminal_write(", Type: ");
|
||||||
itoa(mmap[i].type, buf, 10);
|
itoa(mmap[i].type, buf, 10);
|
||||||
terminal_write(buf);
|
terminal_write(buf);
|
||||||
@@ -75,6 +86,6 @@ void kmain(void) {
|
|||||||
|
|
||||||
// Halt CPU in loop
|
// Halt CPU in loop
|
||||||
while (1) {
|
while (1) {
|
||||||
asm volatile("hlt");
|
__asm__("hlt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "kmalloc.h"
|
#include "kmalloc.h"
|
||||||
#include "terminal.h" // Optional: for debug output
|
#include "terminal.h" // Optional: for debug output
|
||||||
|
|
||||||
|
#define HEAP_END 0xC0500000
|
||||||
|
|
||||||
static uint32_t current_heap = 0;
|
static uint32_t current_heap = 0;
|
||||||
|
|
||||||
// Initialize the allocator with a starting heap address
|
// Initialize the allocator with a starting heap address
|
||||||
@@ -32,7 +34,18 @@ void* kmalloc_aligned(size_t size, uint32_t alignment) {
|
|||||||
current_heap = (current_heap + alignment) & ~(alignment - 1);
|
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;
|
void* addr = (void*)current_heap;
|
||||||
current_heap += size;
|
current_heap += size;
|
||||||
return addr;
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,31 +1,26 @@
|
|||||||
ENTRY(_start)
|
ENTRY(kmain)
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
. = 1M;
|
. = 1M;
|
||||||
|
|
||||||
.multiboot : {
|
|
||||||
*(.multiboot)
|
|
||||||
}
|
|
||||||
|
|
||||||
.text : {
|
.text : {
|
||||||
*(.text)
|
*(.text*)
|
||||||
}
|
|
||||||
|
|
||||||
.rodata : {
|
|
||||||
*(.rodata)
|
|
||||||
}
|
|
||||||
|
|
||||||
.data : {
|
|
||||||
*(.data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rodata : { *(.rodata*) }
|
||||||
|
.data : { *(.data*) }
|
||||||
.bss : {
|
.bss : {
|
||||||
*(.bss)
|
*(.bss*)
|
||||||
*(COMMON)
|
*(COMMON)
|
||||||
}
|
}
|
||||||
|
|
||||||
. = ALIGN(4096);
|
.stack (NOLOAD) : {
|
||||||
__stack_top = .;
|
. = ALIGN(4);
|
||||||
. += 128K;
|
. = . + 0x1000;
|
||||||
__stack_bottom = .;
|
}
|
||||||
|
|
||||||
|
.heap (NOLOAD) : {
|
||||||
|
. = ALIGN(4);
|
||||||
|
. = . + 0x10000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,15 @@ void mark_as_free(void *ptr) {
|
|||||||
block->next = block->next->next;
|
block->next = block->next->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement coalescing with previous block
|
// 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)
|
void *malloc(size_t size)
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
#include "memmap.h"
|
#include "memmap.h"
|
||||||
|
|
||||||
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) {
|
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) {
|
||||||
// Fill with dummy values for now
|
uint32_t count = 0;
|
||||||
map[0].base_addr = 0x00000000;
|
|
||||||
map[0].length = 0x0009FC00;
|
|
||||||
map[0].type = 1;
|
|
||||||
|
|
||||||
map[1].base_addr = 0x00100000;
|
if (max_entries >= 1) {
|
||||||
map[1].length = 0x1FF00000;
|
map[count].base_addr = 0x00000000;
|
||||||
map[1].type = 1;
|
map[count].length = 0x0009FC00;
|
||||||
|
map[count].type = 1;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
return 2; // 2 regions
|
if (max_entries >= 2) {
|
||||||
|
map[count].base_addr = 0x00100000;
|
||||||
|
map[count].length = 0x1FF00000;
|
||||||
|
map[count].type = 1;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
138
kernel/memory.c
Normal file
138
kernel/memory.c
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
/* note: this is a stub, please use care as theres duplicate functions in utils implementation
|
||||||
|
/* --------------------------------------------------------------------- *
|
||||||
|
* Helper: copy a single byte (used by both memcpy and memmove)
|
||||||
|
* --------------------------------------------------------------------- */
|
||||||
|
static inline void byte_copy_forward(uint8_t *dst, const uint8_t *src, size_t n)
|
||||||
|
{
|
||||||
|
while (n--) *dst++ = *src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void byte_copy_backward(uint8_t *dst, const uint8_t *src, size_t n)
|
||||||
|
{
|
||||||
|
dst += n; src += n;
|
||||||
|
while (n--) *--dst = *--src;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------- *
|
||||||
|
* memcpy – no overlap allowed (behaviour undefined if overlap)
|
||||||
|
* --------------------------------------------------------------------- */
|
||||||
|
void *memcpy(void *restrict dst, const void *restrict src, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t *d = (uint8_t *)dst;
|
||||||
|
const uint8_t *s = (const uint8_t *)src;
|
||||||
|
|
||||||
|
#if defined(MEMORY_OPTIMIZED)
|
||||||
|
/* Align destination to 4-byte boundary */
|
||||||
|
size_t align = (uintptr_t)d & 3U;
|
||||||
|
if (align) {
|
||||||
|
size_t head = 4 - align;
|
||||||
|
if (head > n) head = n;
|
||||||
|
byte_copy_forward(d, s, head);
|
||||||
|
d += head; s += head; n -= head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 32-bit word copy – safe because we already aligned dst */
|
||||||
|
{
|
||||||
|
uint32_t *d32 = (uint32_t *)d;
|
||||||
|
const uint32_t *s32 = (const uint32_t *)s;
|
||||||
|
size_t words = n / 4;
|
||||||
|
while (words--) *d32++ = *s32++;
|
||||||
|
d = (uint8_t *)d32;
|
||||||
|
s = (const uint8_t *)s32;
|
||||||
|
n &= 3;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
byte_copy_forward(d, s, n);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------- *
|
||||||
|
* memmove – handles overlapping regions correctly
|
||||||
|
* --------------------------------------------------------------------- */
|
||||||
|
void *memmove(void *dst, const void *src, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t *d = (uint8_t *)dst;
|
||||||
|
const uint8_t *s = (const uint8_t *)src;
|
||||||
|
|
||||||
|
if (n == 0 || dst == src)
|
||||||
|
return dst;
|
||||||
|
|
||||||
|
if (d < s) { /* copy forward */
|
||||||
|
#if defined(MEMORY_OPTIMIZED)
|
||||||
|
/* Same fast path as memcpy when no overlap */
|
||||||
|
size_t align = (uintptr_t)d & 3U;
|
||||||
|
if (align) {
|
||||||
|
size_t head = 4 - align;
|
||||||
|
if (head > n) head = n;
|
||||||
|
byte_copy_forward(d, s, head);
|
||||||
|
d += head; s += head; n -= head;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32_t *d32 = (uint32_t *)d;
|
||||||
|
const uint32_t *s32 = (const uint32_t *)s;
|
||||||
|
size_t words = n / 4;
|
||||||
|
while (words--) *d32++ = *s32++;
|
||||||
|
d = (uint8_t *)d32;
|
||||||
|
s = (const uint8_t *)s32;
|
||||||
|
n &= 3;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
byte_copy_forward(d, s, n);
|
||||||
|
} else { /* copy backward */
|
||||||
|
byte_copy_backward(d, s, n);
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------- *
|
||||||
|
* memcmp – lexicographical compare
|
||||||
|
* --------------------------------------------------------------------- */
|
||||||
|
int memcmp(const void *s1, const void *s2, size_t n)
|
||||||
|
{
|
||||||
|
const uint8_t *a = (const uint8_t *)s1;
|
||||||
|
const uint8_t *b = (const uint8_t *)s2;
|
||||||
|
|
||||||
|
#if defined(MEMORY_OPTIMIZED)
|
||||||
|
/* Align to 4-byte boundary */
|
||||||
|
size_t align = (uintptr_t)a & 3U;
|
||||||
|
if (align && align == ((uintptr_t)b & 3U)) {
|
||||||
|
size_t head = 4 - align;
|
||||||
|
if (head > n) head = n;
|
||||||
|
while (head--) {
|
||||||
|
int diff = *a++ - *b++;
|
||||||
|
if (diff) return diff;
|
||||||
|
}
|
||||||
|
n -= head;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const uint32_t *a32 = (const uint32_t *)a;
|
||||||
|
const uint32_t *b32 = (const uint32_t *)b;
|
||||||
|
size_t words = n / 4;
|
||||||
|
while (words--) {
|
||||||
|
uint32_t va = *a32++, vb = *b32++;
|
||||||
|
if (va != vb) {
|
||||||
|
/* byte-wise fallback for the differing word */
|
||||||
|
const uint8_t *pa = (const uint8_t *)(a32 - 1);
|
||||||
|
const uint8_t *pb = (const uint8_t *)(b32 - 1);
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
int diff = pa[i] - pb[i];
|
||||||
|
if (diff) return diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a = (const uint8_t *)a32;
|
||||||
|
b = (const uint8_t *)b32;
|
||||||
|
n &= 3;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (n--) {
|
||||||
|
int diff = *a++ - *b++;
|
||||||
|
if (diff) return diff;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
25
kernel/memory.h
Normal file
25
kernel/memory.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef MEMORY_H
|
||||||
|
#define MEMORY_H
|
||||||
|
|
||||||
|
#include <stddef.h> /* size_t, NULL */
|
||||||
|
#include <stdint.h> /* uint8_t */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* C11 / POSIX-2004 signatures */
|
||||||
|
void *memcpy(void *restrict dst, const void *restrict src, size_t n);
|
||||||
|
void *memmove(void *dst, const void *src, size_t n);
|
||||||
|
int memcmp(const void *s1, const void *s2, size_t n);
|
||||||
|
|
||||||
|
/* Optional fast-path using 32-bit loads (x86 only) */
|
||||||
|
#if defined(__i386__) && !defined(MEMORY_NO_OPT)
|
||||||
|
# define MEMORY_OPTIMIZED 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MEMORY_H */
|
||||||
27
kernel/mouse.c
Normal file
27
kernel/mouse.c
Normal 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
26
kernel/mouse.h
Normal 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
|
||||||
|
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
page_directory_entry_t *page_directory = (page_directory_entry_t *)0x100000;
|
page_directory_entry_t *page_directory = (page_directory_entry_t *)0x200000;
|
||||||
page_table_entry_t *page_table = (page_table_entry_t *)0x101000;
|
page_table_entry_t *page_table = (page_table_entry_t *)0x201000;
|
||||||
page_table_entry_t *heap_page_table = (page_table_entry_t *)0x102000; // Located right after the page directory
|
page_table_entry_t *heap_page_table = (page_table_entry_t *)0x202000;
|
||||||
|
|
||||||
// Helper function to set up the page directory entry
|
// Helper function to set up the page directory entry
|
||||||
void set_page_directory(page_directory_entry_t *dir) {
|
void set_page_directory(page_directory_entry_t *dir) {
|
||||||
@@ -37,12 +37,12 @@ void enable_paging() {
|
|||||||
uint32_t cr0;
|
uint32_t cr0;
|
||||||
|
|
||||||
// Load page directory into CR3
|
// Load page directory into CR3
|
||||||
asm volatile("mov %0, %%cr3" : : "r"(page_directory));
|
__asm__("mov %0, %%cr3" : : "r"(page_directory));
|
||||||
|
|
||||||
// Enable paging (set the PG bit in CR0)
|
// Enable paging (set the PG bit in CR0)
|
||||||
asm volatile("mov %%cr0, %0" : "=r"(cr0));
|
__asm__("mov %%cr0, %0" : "=r"(cr0));
|
||||||
cr0 |= 0x80000000; // Set the PG (paging) bit
|
cr0 |= 0x80000000; // Set the PG (paging) bit
|
||||||
asm volatile("mov %0, %%cr0" : : "r"(cr0));
|
__asm__("mov %0, %%cr0" : : "r"(cr0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize paging: set up the page directory and enable paging
|
// Initialize paging: set up the page directory and enable paging
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ void panic(const char *message) {
|
|||||||
|
|
||||||
// Halt the system
|
// Halt the system
|
||||||
while (true) {
|
while (true) {
|
||||||
asm volatile ("cli; hlt");
|
__asm__("cli; hlt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
104
kernel/print.c
104
kernel/print.c
@@ -1,6 +1,102 @@
|
|||||||
#include <stdio.h>
|
#include <stdarg.h>
|
||||||
|
#include "print.h"
|
||||||
|
#include "serial.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
void print_string(const char *str)
|
void my_putchar(char ch) {
|
||||||
{
|
// Write a single character to standard output
|
||||||
printf("%s", str);
|
// 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);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
#ifndef PRINT_H
|
#ifndef PRINT_H
|
||||||
#define PRINT_H
|
#define PRINT_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
void print_string(const char *str);
|
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
|
#endif
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// Defined in context_switch.s
|
||||||
|
extern void ctx_switch(uint32_t **old_sp_ptr, uint32_t *new_sp);
|
||||||
|
|
||||||
static task_t tasks[MAX_TASKS];
|
static task_t tasks[MAX_TASKS];
|
||||||
|
|
||||||
|
// Stack memory area. Note: x86 Stacks grow DOWN from high to low addresses.
|
||||||
static uint32_t task_stacks[MAX_TASKS][STACK_SIZE / sizeof(uint32_t)];
|
static uint32_t task_stacks[MAX_TASKS][STACK_SIZE / sizeof(uint32_t)];
|
||||||
|
|
||||||
static int task_count = 0;
|
static int task_count = 0;
|
||||||
@@ -9,7 +14,6 @@ static task_t *task_list = NULL;
|
|||||||
static task_t *current_task = NULL;
|
static task_t *current_task = NULL;
|
||||||
|
|
||||||
void scheduler_init() {
|
void scheduler_init() {
|
||||||
// Initialize task list, etc.
|
|
||||||
task_list = NULL;
|
task_list = NULL;
|
||||||
current_task = NULL;
|
current_task = NULL;
|
||||||
task_count = 0;
|
task_count = 0;
|
||||||
@@ -20,16 +24,42 @@ void scheduler_add_task(void (*entry)(void)) {
|
|||||||
|
|
||||||
task_t *new_task = &tasks[task_count];
|
task_t *new_task = &tasks[task_count];
|
||||||
new_task->id = task_count;
|
new_task->id = task_count;
|
||||||
new_task->entry = entry;
|
|
||||||
|
|
||||||
// Simulate a stack pointer pointing to the "top" of the stack
|
// 1. Calculate the top of the stack (High Address)
|
||||||
new_task->stack_ptr = &task_stacks[task_count][STACK_SIZE / sizeof(uint32_t) - 1];
|
// We point to the very end of the array.
|
||||||
|
uint32_t *sp = &task_stacks[task_count][STACK_SIZE / sizeof(uint32_t)];
|
||||||
|
|
||||||
|
// 2. "Forge" the stack frame to look like ctx_switch saved it.
|
||||||
|
// We push values onto the stack by decrementing the pointer and writing.
|
||||||
|
|
||||||
|
// --- Return Address (EIP) ---
|
||||||
|
sp--;
|
||||||
|
*sp = (uint32_t)entry; // When ctx_switch does 'ret', it pops this and jumps to 'entry'
|
||||||
|
|
||||||
|
// --- EFLAGS ---
|
||||||
|
sp--;
|
||||||
|
*sp = 0x00000202; // Reserved bit set, Interrupts Enabled (IF=1). Important!
|
||||||
|
|
||||||
|
// --- General Purpose Registers (PUSHA/POPA layout) ---
|
||||||
|
// Order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI
|
||||||
|
// We initialize them to 0 or meaningful values.
|
||||||
|
sp--; *sp = 0; // EAX
|
||||||
|
sp--; *sp = 0; // ECX
|
||||||
|
sp--; *sp = 0; // EDX
|
||||||
|
sp--; *sp = 0; // EBX
|
||||||
|
sp--; *sp = 0; // ESP (Ignored by POPA)
|
||||||
|
sp--; *sp = 0; // EBP
|
||||||
|
sp--; *sp = 0; // ESI
|
||||||
|
sp--; *sp = 0; // EDI
|
||||||
|
|
||||||
|
// Save this final stack location to the TCB
|
||||||
|
new_task->stack_ptr = sp;
|
||||||
new_task->next = NULL;
|
new_task->next = NULL;
|
||||||
|
|
||||||
// Add to task list
|
// 3. Add to linked list
|
||||||
if (task_list == NULL) {
|
if (task_list == NULL) {
|
||||||
task_list = new_task;
|
task_list = new_task;
|
||||||
|
current_task = new_task; // Make sure we have a current task to start
|
||||||
} else {
|
} else {
|
||||||
task_t *tail = task_list;
|
task_t *tail = task_list;
|
||||||
while (tail->next) {
|
while (tail->next) {
|
||||||
@@ -42,21 +72,25 @@ void scheduler_add_task(void (*entry)(void)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void scheduler_schedule() {
|
void scheduler_schedule() {
|
||||||
// Very basic round-robin switch
|
if (!current_task) return;
|
||||||
if (current_task && current_task->next) {
|
|
||||||
|
task_t *prev = current_task;
|
||||||
|
|
||||||
|
// Round-robin logic
|
||||||
|
if (current_task->next) {
|
||||||
current_task = current_task->next;
|
current_task = current_task->next;
|
||||||
} else {
|
} else {
|
||||||
current_task = task_list; // Loop back
|
current_task = task_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call context switch or simulate yielding to current_task
|
// Perform the ACTUAL context switch
|
||||||
// In real system: context_switch_to(current_task)
|
// We pass the address of the previous task's stack pointer storage
|
||||||
if (current_task && current_task->entry) {
|
// and the value of the new task's stack pointer.
|
||||||
current_task->entry(); // Simulate switching by calling
|
if (prev != current_task) {
|
||||||
|
ctx_switch(&prev->stack_ptr, current_task->stack_ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler_yield() {
|
void scheduler_yield() {
|
||||||
// Stub: manually call schedule for cooperative multitasking
|
|
||||||
scheduler_schedule();
|
scheduler_schedule();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,21 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define MAX_TASKS 8
|
#define MAX_TASKS 8
|
||||||
#define STACK_SIZE 1024
|
#define STACK_SIZE 1024 // in bytes
|
||||||
|
|
||||||
typedef struct task {
|
typedef struct task {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
void (*entry)(void);
|
|
||||||
|
// The most important field:
|
||||||
|
// Where was the stack pointer when we last left this task?
|
||||||
uint32_t *stack_ptr;
|
uint32_t *stack_ptr;
|
||||||
|
|
||||||
struct task *next;
|
struct task *next;
|
||||||
} task_t;
|
} task_t;
|
||||||
|
|
||||||
void scheduler_init();
|
void scheduler_init();
|
||||||
void scheduler_add_task(void (*entry)(void));
|
void scheduler_add_task(void (*entry)(void));
|
||||||
void scheduler_schedule();
|
void scheduler_schedule();
|
||||||
void scheduler_yield(); // Optional for cooperative scheduling
|
void scheduler_yield();
|
||||||
|
|
||||||
#endif // SCHEDULER_H
|
#endif // SCHEDULER_H
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#define COM1 0x3F8
|
#define COM1 0x3F8
|
||||||
#define COM2 0x2F8
|
#define COM2 0x2F8
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
#include "shell.h"
|
#include "shell.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include <stdio.h>
|
#include "print.h"
|
||||||
#include <string.h>
|
#include "string_utils.h"
|
||||||
|
|
||||||
void execute(char *input) {
|
void execute(char *input) {
|
||||||
if (strcmp(input, "help") == 0) {
|
if (my_strcmp(input, "help") == 0) {
|
||||||
printf("Available commands: help, clear, exit\n");
|
my_printf("Available commands: help, clear, exit\n");
|
||||||
} else if (strcmp(input, "clear") == 0) {
|
} else if (my_strcmp(input, "clear") == 0) {
|
||||||
terminal_clear();
|
terminal_clear();
|
||||||
} else {
|
} else {
|
||||||
printf("Unknown command: %s\n", input);
|
my_printf("Unknown command: %s\n", input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void shell_loop()
|
void shell_loop()
|
||||||
{
|
{
|
||||||
char input[256];
|
char input[256];
|
||||||
int index = 0;
|
size_t index = 0;
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
printf("> ");
|
my_printf("> ");
|
||||||
index = 0;
|
index = 0;
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
@@ -32,7 +32,7 @@ void shell_loop()
|
|||||||
if (c == '\n' || c == '\r') // Enter key
|
if (c == '\n' || c == '\r') // Enter key
|
||||||
{
|
{
|
||||||
input[index] = '\0';
|
input[index] = '\0';
|
||||||
printf("\n");
|
my_printf("\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (c == '\b' || c == 127) // Backspace
|
else if (c == '\b' || c == 127) // Backspace
|
||||||
@@ -40,19 +40,19 @@ void shell_loop()
|
|||||||
if (index > 0)
|
if (index > 0)
|
||||||
{
|
{
|
||||||
index--;
|
index--;
|
||||||
printf("\b \b"); // Erase last char on screen
|
my_printf("\b \b"); // Erase last char on screen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (index < sizeof(input) - 1) {
|
if (index < sizeof(input) - 1) {
|
||||||
input[index++] = c;
|
input[index++] = c;
|
||||||
putchar(c);
|
terminal_putchar(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(input, "exit") == 0)
|
if (my_strcmp(input, "exit") == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
execute(input);
|
execute(input);
|
||||||
|
|||||||
80
kernel/string_utils.c
Normal file
80
kernel/string_utils.c
Normal 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
11
kernel/string_utils.h
Normal 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
|
||||||
@@ -21,8 +21,7 @@ void syscall_handler(int code, va_list args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void syscall(int code, ...)
|
void syscall(int code, ...) {
|
||||||
{
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, code);
|
va_start(args, code);
|
||||||
syscall_handler(code, args);
|
syscall_handler(code, args);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef SYSCALLS_H
|
#ifndef SYSCALLS_H
|
||||||
#define SYSCALLS_H
|
#define SYSCALLS_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
// Syscall numbers
|
// Syscall numbers
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SYSCALL_INIT = 0,
|
SYSCALL_INIT = 0,
|
||||||
@@ -9,8 +10,7 @@ typedef enum {
|
|||||||
} syscall_code_t;
|
} syscall_code_t;
|
||||||
|
|
||||||
// Syscall dispatcher
|
// Syscall dispatcher
|
||||||
void syscall_handler();
|
void syscall_handler(int code, va_list args);
|
||||||
|
|
||||||
// Syscall interface
|
// Syscall interface
|
||||||
void syscall(int code, ...);
|
void syscall(int code, ...);
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ static uint16_t* const vga_buffer = (uint16_t*) VGA_ADDRESS;
|
|||||||
static uint8_t cursor_x = 0;
|
static uint8_t cursor_x = 0;
|
||||||
static uint8_t cursor_y = 0;
|
static uint8_t cursor_y = 0;
|
||||||
static uint8_t current_color = WHITE_ON_BLACK;
|
static uint8_t current_color = WHITE_ON_BLACK;
|
||||||
|
static uint16_t last_cursor_pos = 0xFFFF;
|
||||||
|
|
||||||
void terminal_initialize(void) {
|
void terminal_initialize(void) {
|
||||||
for (uint16_t y = 0; y < VGA_HEIGHT; y++) {
|
for (uint16_t y = 0; y < VGA_HEIGHT; y++) {
|
||||||
@@ -96,8 +97,10 @@ void terminal_clear(void) {
|
|||||||
update_cursor();
|
update_cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_cursor() {
|
void update_cursor(void) {
|
||||||
uint16_t pos = cursor_y * VGA_WIDTH + cursor_x;
|
uint16_t pos = cursor_y * VGA_WIDTH + cursor_x;
|
||||||
|
if (pos == last_cursor_pos) return;
|
||||||
|
last_cursor_pos = pos;
|
||||||
|
|
||||||
outb(0x3D4, 0x0F);
|
outb(0x3D4, 0x0F);
|
||||||
outb(0x3D5, (uint8_t)(pos & 0xFF));
|
outb(0x3D5, (uint8_t)(pos & 0xFF));
|
||||||
|
|||||||
@@ -1,5 +1,119 @@
|
|||||||
|
#include "malloc.h"
|
||||||
|
#include "print.h"
|
||||||
#include "threading.h"
|
#include "threading.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
void threading_init() {
|
#define MAX_THREADS 16 // Maximum number of threads
|
||||||
// Threading initialization code
|
#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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,36 @@
|
|||||||
#ifndef THREADING_H
|
#ifndef THREADING_H
|
||||||
#define THREADING_H
|
#define THREADING_H
|
||||||
|
|
||||||
void threading_init();
|
#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
|
#endif // THREADING_H
|
||||||
|
|||||||
@@ -2,18 +2,23 @@
|
|||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "isr.h"
|
#include "isr.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
static uint32_t tick = 0;
|
static volatile uint32_t tick = 0;
|
||||||
|
|
||||||
void timer_callback(void) {
|
void timer_callback(void) {
|
||||||
tick++;
|
tick++;
|
||||||
|
|
||||||
// Print every 100 ticks for debugging purposes
|
|
||||||
if (tick % 100 == 0) {
|
if (tick % 100 == 0) {
|
||||||
char tick_msg[50];
|
char buf[16];
|
||||||
snprintf(tick_msg, sizeof(tick_msg), "Tick count: %u\n", tick);
|
itoa(tick, buf, 10);
|
||||||
terminal_write(tick_msg);
|
terminal_write("Tick count: ");
|
||||||
|
terminal_write(buf);
|
||||||
|
terminal_write("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outb(0x20, 0x20); // EOI to PIC
|
||||||
}
|
}
|
||||||
|
|
||||||
void timer_init(uint32_t frequency) {
|
void timer_init(uint32_t frequency) {
|
||||||
|
|||||||
1
kernel/types.c
Normal file
1
kernel/types.c
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "types.h"
|
||||||
62
kernel/types.h
Normal file
62
kernel/types.h
Normal 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
64
kernel/usb.c
Normal 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
24
kernel/usb.h
Normal 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
|
||||||
@@ -77,13 +77,9 @@ char* utoa(unsigned int value, char* str, int base) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_hex(uint32_t val) {
|
void *memset(void *dest, int value, size_t len) {
|
||||||
char hex_chars[] = "0123456789ABCDEF";
|
unsigned char *ptr = (unsigned char *)dest;
|
||||||
char buf[11] = "0x00000000";
|
while (len-- > 0)
|
||||||
for (int i = 9; i >= 2; i--) {
|
*ptr++ = (unsigned char)value;
|
||||||
buf[i] = hex_chars[val & 0xF];
|
return dest;
|
||||||
val >>= 4;
|
|
||||||
}
|
|
||||||
terminal_write(buf);
|
|
||||||
serial_write(buf);
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef UTILS_H
|
#ifndef UTILS_H
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
// Convert integer to string (base is typically 10, 16, etc.)
|
// Convert integer to string (base is typically 10, 16, etc.)
|
||||||
char* itoa(int value, char* str, int base);
|
char* itoa(int value, char* str, int base);
|
||||||
@@ -9,6 +9,6 @@ char* itoa(int value, char* str, int base);
|
|||||||
// Convert unsigned integer to string (base is typically 10, 16, etc.)
|
// Convert unsigned integer to string (base is typically 10, 16, etc.)
|
||||||
char* utoa(unsigned int value, char* str, int base);
|
char* utoa(unsigned int value, char* str, int base);
|
||||||
|
|
||||||
void print_hex(uint32_t val);
|
void *memset(void *dest, int value, size_t len);
|
||||||
|
|
||||||
#endif // UTILS_H
|
#endif // UTILS_H
|
||||||
|
|||||||
142
kernel/vga.c
142
kernel/vga.c
@@ -1,4 +1,20 @@
|
|||||||
#include "vga.h"
|
#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) {
|
uint8_t vga_entry_color(vga_color fg, vga_color bg) {
|
||||||
return fg | bg << 4;
|
return fg | bg << 4;
|
||||||
@@ -7,3 +23,129 @@ uint8_t vga_entry_color(vga_color fg, vga_color bg) {
|
|||||||
uint16_t vga_entry(unsigned char uc, uint8_t color) {
|
uint16_t vga_entry(unsigned char uc, uint8_t color) {
|
||||||
return (uint16_t)uc | (uint16_t)color << 8;
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
25
kernel/vga.h
25
kernel/vga.h
@@ -2,7 +2,11 @@
|
|||||||
#define VGA_H
|
#define VGA_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdarg.h> // For va_list, va_start, etc.
|
||||||
|
|
||||||
|
// VGA color definitions
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VGA_COLOR_BLACK = 0,
|
VGA_COLOR_BLACK = 0,
|
||||||
VGA_COLOR_BLUE = 1,
|
VGA_COLOR_BLUE = 1,
|
||||||
@@ -22,7 +26,28 @@ typedef enum {
|
|||||||
VGA_COLOR_WHITE = 15,
|
VGA_COLOR_WHITE = 15,
|
||||||
} vga_color;
|
} 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);
|
uint8_t vga_entry_color(vga_color fg, vga_color bg);
|
||||||
uint16_t vga_entry(unsigned char uc, uint8_t color);
|
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
|
#endif
|
||||||
Reference in New Issue
Block a user