mirror of
https://github.com/gbowne1/ClassicOS.git
synced 2026-01-21 12:35:19 -08:00
Compare commits
62 Commits
9aa1b85ca0
...
gbowne1-pa
| Author | SHA1 | Date | |
|---|---|---|---|
| 09b2b8cd11 | |||
| 4079d18a45 | |||
| b6c158957e | |||
|
|
a7b0d1152f | ||
|
|
e38f1aa2ee | ||
|
|
78d5e9a7ab | ||
|
|
daead5ee57 | ||
|
|
4fb81d2e57 | ||
| 19f7c7b213 | |||
| bc9d84a93e | |||
| 9066ceaddb | |||
|
|
3b67e81ed0 | ||
|
|
841892398a | ||
| 86608ef48c | |||
| 785c8920d8 | |||
| c0e7ab6be0 | |||
| f78bc27f35 | |||
| 507b4f5511 | |||
|
|
12046ce96b | ||
|
|
a9b8ac7066 | ||
| d6ab8c91f8 | |||
|
|
35ebd5fd72 | ||
| 10d3761be1 | |||
| cc2e967a4d | |||
| 9eae2e1005 | |||
|
|
bd4236ad9b | ||
| 5292808934 | |||
|
|
09c48c2f50 | ||
| caea475daf | |||
|
|
f30be3ddd5 | ||
| d83e247bbd | |||
|
|
a1a6fd2aa9 | ||
|
|
66f9056406 | ||
|
|
45acbb5c04 | ||
|
|
649a227e41 | ||
| 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 |
24
.clang-format
Normal file
24
.clang-format
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
BasedOnStyle: Google
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
ColumnLimit: 80
|
||||||
|
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReferenceAlignment: Right
|
||||||
|
|
||||||
|
AlignConsecutiveMacros: Consecutive
|
||||||
|
AlignTrailingComments:
|
||||||
|
Kind: Always
|
||||||
|
OverEmptyLines: 0
|
||||||
|
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IncludeCategories:
|
||||||
|
# Std headers
|
||||||
|
- Regex: '<[[:alnum:]_.]+>'
|
||||||
|
Priority: 2
|
||||||
|
|
||||||
|
# Other headers
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
6
.clangd
Normal file
6
.clangd
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
CompileFlags:
|
||||||
|
CompilationDatabase: build
|
||||||
|
|
||||||
|
Diagnostics:
|
||||||
|
UnusedIncludes: Strict
|
||||||
|
MissingIncludes: Strict
|
||||||
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 8
|
||||||
|
tab_width = 8
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1 +1,5 @@
|
|||||||
|
.build.env
|
||||||
build
|
build
|
||||||
|
cross
|
||||||
|
.cache/
|
||||||
|
compile_commands.json
|
||||||
|
|||||||
103
Makefile
103
Makefile
@@ -1,64 +1,77 @@
|
|||||||
AS = nasm
|
AS = nasm
|
||||||
CC = gcc
|
ASFLAGS = -f elf32 -g -F dwarf
|
||||||
CFLAGS = -std=c11 -m32 -ffreestanding -c -fno-stack-protector -fno-pie
|
CC = i386-elf-gcc
|
||||||
LD = ld
|
LD = i386-elf-ld
|
||||||
QEMU = qemu-system-i386
|
QEMU= qemu-system-i386
|
||||||
IMG_SIZE = 1440k
|
OBJCOPY = i386-elf-objcopy
|
||||||
|
|
||||||
BUILD_DIR = build
|
BUILD_DIR = build
|
||||||
|
CROSS_DIR = cross
|
||||||
BOOT_SRC = bootloader/boot.asm
|
DISK_IMG = $(BUILD_DIR)/disk.img
|
||||||
BOOT_OBJ = $(BUILD_DIR)/boot.o
|
STAGE2_SIZE = 2048
|
||||||
BOOT_ELF = $(BUILD_DIR)/boot.elf
|
|
||||||
BOOT_IMG = $(BUILD_DIR)/boot.img
|
|
||||||
|
|
||||||
KERNEL_C_SRC = $(wildcard kernel/*.c)
|
KERNEL_C_SRC = $(wildcard kernel/*.c)
|
||||||
KERNEL_ASM_SRC = $(wildcard kernel/*.asm)
|
KERNEL_ASM_SRC = $(wildcard kernel/*.asm)
|
||||||
KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC))
|
KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC))
|
||||||
KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC))
|
KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC))
|
||||||
KERNEL_OBJ += $(BUILD_DIR)/boot1.o
|
|
||||||
KERNEL_ELF = $(BUILD_DIR)/kernel.elf
|
|
||||||
KERNEL_BIN = $(BUILD_DIR)/kernel.bin
|
|
||||||
|
|
||||||
DISK_IMG = $(BUILD_DIR)/disk.img
|
KLIBC_SRC = $(wildcard klibc/src/*.c)
|
||||||
|
KLIBC_OBJ = $(patsubst klibc/src/%.c, $(BUILD_DIR)/klibc/%.o, $(KLIBC_SRC))
|
||||||
|
|
||||||
all: $(BOOT_IMG) $(KERNEL_BIN) $(DISK_IMG)
|
.PHONY: all stage1 stage2 kernel compile-commands $(BUILD_DIR)/compile_commands.json run gdb clean clean-cross clean-all
|
||||||
|
all: $(DISK_IMG)
|
||||||
|
|
||||||
|
stage1: $(BUILD_DIR)
|
||||||
|
$(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
|
||||||
|
|
||||||
|
# NOTE: Stage2 final size should be checked against `$(STAGE2_SIZE)` by the build system to avoid an overflow.
|
||||||
|
# Alternatively, convey the final stage2 size through other means to stage1.
|
||||||
|
stage2: $(BUILD_DIR)
|
||||||
|
$(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm
|
||||||
|
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -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
|
||||||
|
|
||||||
|
$(BUILD_DIR)/asm_%.o: kernel/%.asm
|
||||||
|
$(AS) $(ASFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.o: kernel/%.c
|
||||||
|
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $<
|
||||||
|
|
||||||
|
$(BUILD_DIR)/klibc/%.o: klibc/src/%.c
|
||||||
|
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $<
|
||||||
|
|
||||||
|
kernel: $(KERNEL_OBJ) | $(BUILD_DIR) $(KLIBC_OBJ)
|
||||||
|
$(LD) -melf_i386 -Tkernel/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ) $(KLIBC_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):
|
$(BUILD_DIR):
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
mkdir -p $(BUILD_DIR)/klibc
|
||||||
|
|
||||||
$(BOOT_OBJ): $(BOOT_SRC) | $(BUILD_DIR)
|
compile-commands: $(BUILD_DIR)/compile_commands.json
|
||||||
$(AS) -f elf32 -g -F dwarf -o $@ $<
|
$(BUILD_DIR)/compile_commands.json: $(BUILD_DIR)
|
||||||
|
bear --output $@ -- make -B
|
||||||
|
|
||||||
$(BOOT_ELF): $(BOOT_OBJ)
|
run:
|
||||||
$(LD) -Ttext=0x7c00 -melf_i386 -o $@ $<
|
qemu-system-i386 -s -S $(DISK_IMG)
|
||||||
|
|
||||||
$(BOOT_IMG): $(BOOT_ELF)
|
gdb:
|
||||||
objcopy -O binary $< $@
|
gdb -x gdb.txt
|
||||||
truncate -s $(IMG_SIZE) $@
|
|
||||||
|
|
||||||
$(BUILD_DIR)/boot1.o: bootloader/boot1.asm
|
|
||||||
$(AS) -f elf32 -o $@ $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/asm_%.o: kernel/%.asm
|
|
||||||
$(AS) -f elf32 -o $@ $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: kernel/%.c
|
|
||||||
$(CC) $(CFLAGS) $< -o $@
|
|
||||||
|
|
||||||
$(KERNEL_BIN): $(KERNEL_OBJ) | $(BUILD_DIR)
|
|
||||||
$(LD) -melf_i386 --oformat binary -T bootloader/linker.ld -o $@ $(KERNEL_OBJ)
|
|
||||||
|
|
||||||
$(DISK_IMG): $(BOOT_IMG) $(KERNEL_BIN)
|
|
||||||
dd if=$(BOOT_IMG) of=$@ bs=512 seek=4
|
|
||||||
dd if=$(KERNEL_BIN) of=$@ bs=512 seek=200
|
|
||||||
|
|
||||||
run: $(DISK_IMG)
|
|
||||||
$(QEMU) -drive file=$<,format=raw,if=floppy
|
|
||||||
|
|
||||||
.PHONY: stage1 clean
|
|
||||||
|
|
||||||
stage1: $(BOOT_IMG)
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
|
clean-cross:
|
||||||
|
rm -rf $(CROSS_DIR)
|
||||||
|
rm -rf .build.env
|
||||||
|
|
||||||
|
clean-all: clean clean-cross
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -35,6 +35,7 @@ You’ll need the following tools installed:
|
|||||||
- `qemu-system-i386`
|
- `qemu-system-i386`
|
||||||
|
|
||||||
Optional:
|
Optional:
|
||||||
|
|
||||||
- `gdb`
|
- `gdb`
|
||||||
- `vncviewer` (TigerVNC or similar)
|
- `vncviewer` (TigerVNC or similar)
|
||||||
|
|
||||||
@@ -42,13 +43,27 @@ Optional:
|
|||||||
|
|
||||||
## 🛠️ Building ClassicOS
|
## 🛠️ Building ClassicOS
|
||||||
|
|
||||||
Clone and build:
|
Clone repository:
|
||||||
|
|
||||||
```bash
|
```sh
|
||||||
git clone https://github.com/gbowne1/ClassicOS.git
|
git clone https://github.com/gbowne1/ClassicOS.git
|
||||||
cd ClassicOS
|
cd ClassicOS
|
||||||
make
|
|
||||||
```
|
```
|
||||||
|
|
||||||
build kernel
|
Run `configure` script to build a cross-compiler toolchain for `i386-elf`:
|
||||||
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"
|
|
||||||
|
```sh
|
||||||
|
./configure
|
||||||
|
```
|
||||||
|
|
||||||
|
Source the `.build.env` file to add the cross-compiler toolchain to your PATH:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
source .build.env
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the kernel:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|||||||
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,203 +0,0 @@
|
|||||||
; ==============================================================================
|
|
||||||
; boot1.asm - Second Stage Bootloader (Fixed Real Mode Transition)
|
|
||||||
; ==============================================================================
|
|
||||||
|
|
||||||
[BITS 32]
|
|
||||||
global _start
|
|
||||||
extern kmain
|
|
||||||
|
|
||||||
_start:
|
|
||||||
; Set up segments
|
|
||||||
mov ax, 0x10
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov fs, ax
|
|
||||||
mov gs, ax
|
|
||||||
mov ss, ax
|
|
||||||
|
|
||||||
; Stack (must be identity-mapped)
|
|
||||||
mov esp, 0x90000
|
|
||||||
|
|
||||||
; CPU Feature Detection: check CPUID support
|
|
||||||
pushfd ; Save flags
|
|
||||||
pop eax
|
|
||||||
mov ecx, eax
|
|
||||||
xor eax, 1 << 21 ; Flip ID bit
|
|
||||||
push eax
|
|
||||||
popfd
|
|
||||||
pushfd
|
|
||||||
pop eax
|
|
||||||
xor eax, ecx
|
|
||||||
jz .no_cpuid ; CPUID unsupported if no change
|
|
||||||
|
|
||||||
; CPUID supported, verify features
|
|
||||||
mov eax, 1
|
|
||||||
cpuid
|
|
||||||
; Check for paging support (bit 31 of edx)
|
|
||||||
test edx, 1 << 31
|
|
||||||
jz .no_paging_support
|
|
||||||
|
|
||||||
; Additional CPU feature checks could be added here
|
|
||||||
|
|
||||||
jmp .cpuid_check_done
|
|
||||||
|
|
||||||
.no_cpuid:
|
|
||||||
mov si, no_cpuid_msg
|
|
||||||
call print_string_16
|
|
||||||
jmp halt
|
|
||||||
|
|
||||||
.no_paging_support:
|
|
||||||
mov si, no_paging_msg
|
|
||||||
call print_string_16
|
|
||||||
jmp halt
|
|
||||||
|
|
||||||
.cpuid_check_done:
|
|
||||||
|
|
||||||
; Temporarily switch back to real mode
|
|
||||||
cli
|
|
||||||
mov eax, cr0
|
|
||||||
and eax, 0x7FFFFFFE ; Clear PE & PG bits
|
|
||||||
mov cr0, eax
|
|
||||||
jmp 0x18:real_mode_entry
|
|
||||||
|
|
||||||
; ----------------------------------------------------------------
|
|
||||||
[BITS 16]
|
|
||||||
real_mode_entry:
|
|
||||||
; Real mode for BIOS access (E820, VESA)
|
|
||||||
xor ax, ax
|
|
||||||
mov es, ax
|
|
||||||
|
|
||||||
; VESA call
|
|
||||||
mov di, VbeControllerInfo
|
|
||||||
mov ax, 0x4F00
|
|
||||||
int 0x10
|
|
||||||
jc vesa_error
|
|
||||||
|
|
||||||
; E820 memory map
|
|
||||||
xor ebx, ebx
|
|
||||||
mov edx, 0x534D4150
|
|
||||||
mov di, MemoryMapBuffer
|
|
||||||
mov [MemoryMapEntries], dword 0
|
|
||||||
|
|
||||||
.e820_loop:
|
|
||||||
mov eax, 0xE820
|
|
||||||
mov ecx, 24
|
|
||||||
int 0x15
|
|
||||||
jc e820_error
|
|
||||||
add di, 24
|
|
||||||
inc dword [MemoryMapEntries]
|
|
||||||
test ebx, ebx
|
|
||||||
jnz .e820_loop
|
|
||||||
jmp e820_done
|
|
||||||
|
|
||||||
e820_error:
|
|
||||||
mov si, e820_error_msg
|
|
||||||
call print_string_16
|
|
||||||
jmp halt
|
|
||||||
|
|
||||||
vesa_error:
|
|
||||||
mov si, vesa_error_msg
|
|
||||||
call print_string_16
|
|
||||||
|
|
||||||
; Fallback: set VGA text mode 3 and continue
|
|
||||||
mov ah, 0x00 ; BIOS Set Video Mode function
|
|
||||||
mov al, 0x03 ; VGA 80x25 text mode
|
|
||||||
int 0x10
|
|
||||||
|
|
||||||
; Clear screen
|
|
||||||
mov ah, 0x06 ; Scroll up function
|
|
||||||
mov al, 0 ; Clear entire screen
|
|
||||||
mov bh, 0x07 ; Text attribute (gray on black)
|
|
||||||
mov cx, 0 ; Upper-left corner
|
|
||||||
mov dx, 0x184F ; Lower-right corner
|
|
||||||
int 0x10
|
|
||||||
|
|
||||||
jmp e820_done ; Continue booting without VESA graphics
|
|
||||||
|
|
||||||
e820_done:
|
|
||||||
; Back to protected mode
|
|
||||||
cli
|
|
||||||
mov eax, cr0
|
|
||||||
or eax, 1
|
|
||||||
mov cr0, eax
|
|
||||||
jmp 0x08:protected_entry
|
|
||||||
|
|
||||||
; ----------------------------------------------------------------
|
|
||||||
[BITS 16]
|
|
||||||
print_string_16:
|
|
||||||
.loop:
|
|
||||||
lodsb
|
|
||||||
or al, al
|
|
||||||
jz .done
|
|
||||||
mov ah, 0x0E
|
|
||||||
int 0x10
|
|
||||||
jmp .loop
|
|
||||||
.done:
|
|
||||||
ret
|
|
||||||
|
|
||||||
e820_error_msg db "E820 Failed!", 0
|
|
||||||
vesa_error_msg db "VESA Failed!", 0
|
|
||||||
no_cpuid_msg db "No CPUID support detected!", 0
|
|
||||||
no_paging_msg db "CPU lacks paging support!", 0
|
|
||||||
|
|
||||||
; ----------------------------------------------------------------
|
|
||||||
[BITS 32]
|
|
||||||
protected_entry:
|
|
||||||
; Paging setup
|
|
||||||
xor eax, eax
|
|
||||||
mov edi, page_directory
|
|
||||||
mov ecx, 1024
|
|
||||||
rep stosd
|
|
||||||
mov edi, page_table
|
|
||||||
rep stosd
|
|
||||||
mov eax, page_table
|
|
||||||
or eax, 0x3
|
|
||||||
mov [page_directory], eax
|
|
||||||
mov ecx, 1024
|
|
||||||
mov edi, page_table
|
|
||||||
mov eax, 0x00000003
|
|
||||||
.fill_pages:
|
|
||||||
mov [edi], eax
|
|
||||||
add eax, 0x1000
|
|
||||||
add edi, 4
|
|
||||||
loop .fill_pages
|
|
||||||
|
|
||||||
mov eax, page_directory
|
|
||||||
mov cr3, eax
|
|
||||||
mov eax, cr0
|
|
||||||
or eax, 0x80000000
|
|
||||||
mov cr0, eax
|
|
||||||
|
|
||||||
jmp kmain
|
|
||||||
|
|
||||||
halt:
|
|
||||||
cli
|
|
||||||
|
|
||||||
.hang:
|
|
||||||
hlt
|
|
||||||
jmp .hang
|
|
||||||
|
|
||||||
; ----------------------------------------------------------------
|
|
||||||
; Data buffers and variables must be appropriately defined in your data section
|
|
||||||
MemoryMapBuffer times 128 db 0 ; 128*24 bytes reserved for E820 memory map (adjust size as needed)
|
|
||||||
MemoryMapEntries dd 0
|
|
||||||
VbeControllerInfo times 512 db 0 ; Buffer for VESA controller info (adjust size as needed)
|
|
||||||
|
|
||||||
; Define page directory and page table aligned as needed (in your data section)
|
|
||||||
align 4096
|
|
||||||
page_directory times 1024 dd 0
|
|
||||||
align 4096
|
|
||||||
page_table times 1024 dd 0
|
|
||||||
|
|
||||||
%assign pad_size 4096
|
|
||||||
%ifdef __SIZE__
|
|
||||||
%define size_current __SIZE__
|
|
||||||
%else
|
|
||||||
%define size_current ($ - $$)
|
|
||||||
%endif
|
|
||||||
|
|
||||||
%if size_current < pad_size
|
|
||||||
times pad_size - size_current db 0
|
|
||||||
%endif
|
|
||||||
|
|
||||||
checksum_byte db 0
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
ENTRY(kmain)
|
|
||||||
|
|
||||||
SECTIONS {
|
|
||||||
. = 1M;
|
|
||||||
|
|
||||||
.text : {
|
|
||||||
*(.text*)
|
|
||||||
}
|
|
||||||
|
|
||||||
.rodata : { *(.rodata*) }
|
|
||||||
.data : { *(.data*) }
|
|
||||||
.bss : {
|
|
||||||
*(.bss*)
|
|
||||||
*(COMMON)
|
|
||||||
}
|
|
||||||
|
|
||||||
.stack (NOLOAD) : {
|
|
||||||
. = ALIGN(4);
|
|
||||||
. = . + 0x1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.heap (NOLOAD) : {
|
|
||||||
. = ALIGN(4);
|
|
||||||
. = . + 0x10000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,10 +2,16 @@
|
|||||||
; boot.asm - First Stage Bootloader (CHS Based)
|
; boot.asm - First Stage Bootloader (CHS Based)
|
||||||
; ==============================================================================
|
; ==============================================================================
|
||||||
|
|
||||||
[BITS 16]
|
; Params for stage2
|
||||||
; [ORG 0x7C00]
|
%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
|
||||||
|
|
||||||
start:
|
[BITS 16]
|
||||||
|
global _start
|
||||||
|
|
||||||
|
_start:
|
||||||
cli ; Disable interrupts
|
cli ; Disable interrupts
|
||||||
|
|
||||||
mov [bootdev], dl ; Save boot device number (from BIOS in DL)
|
mov [bootdev], dl ; Save boot device number (from BIOS in DL)
|
||||||
@@ -20,29 +26,16 @@ start:
|
|||||||
mov ds, ax
|
mov ds, ax
|
||||||
mov es, ax
|
mov es, ax
|
||||||
|
|
||||||
|
; Query bios for disk parameters
|
||||||
|
call get_disk_params
|
||||||
|
|
||||||
; Load second-stage bootloader (boot1.asm) to 0x7E00
|
; Load second-stage bootloader (boot1.asm) to 0x7E00
|
||||||
mov ax, 1 ; LBA of boot1.asm (starts at sector 1)
|
mov ax, 1 ; LBA of boot1.asm (starts at sector 1)
|
||||||
call lba_to_chs
|
call lba_to_chs
|
||||||
mov al, 4 ; Number of sectors to read
|
mov al, s2_nsect ; Number of sectors to read
|
||||||
mov bx, 0x7E00 ; Destination address offset ES = 0 (0x0000:0x7E00)
|
mov bx, 0x7E00 ; Destination address offset ES = 0 (0x0000:0x7E00)
|
||||||
call read_chs
|
call read_chs
|
||||||
|
|
||||||
; Load kernel to 0x100000 (1 MB)
|
|
||||||
mov ax, 0xffff
|
|
||||||
mov es, ax ; Set ES for destination address (0xffff << 4 == 0xffff0)
|
|
||||||
mov ax, 5 ; LBA of kernel start (boot1 is 4 sectors: LBA 1–4 → kernel at LBA 5)
|
|
||||||
call lba_to_chs
|
|
||||||
mov al, 16 ; Number of sectors for kernel
|
|
||||||
mov bx, 0x10 ; Destination address offset (0xffff:0x10 = 0xffff << 4 + 0x10 = 0x100000)
|
|
||||||
call read_chs
|
|
||||||
jc disk_error
|
|
||||||
|
|
||||||
; Memory Validation: Verify checksum of second stage bootloader
|
|
||||||
mov si, 0x7E00 ; Start of second stage
|
|
||||||
mov cx, 512 * 4 ; Size in bytes (adjust if more sectors loaded)
|
|
||||||
call verify_checksum
|
|
||||||
jc disk_error ; Jump if checksum fails
|
|
||||||
|
|
||||||
; Enable A20 line
|
; Enable A20 line
|
||||||
call enable_a20
|
call enable_a20
|
||||||
jc a20_error ; Jump if A20 enable fails
|
jc a20_error ; Jump if A20 enable fails
|
||||||
@@ -95,17 +88,32 @@ verify_checksum:
|
|||||||
pop ax
|
pop ax
|
||||||
ret
|
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
|
; CHS Disk Read Routine
|
||||||
; AL = number of sectors
|
; AL = number of sectors
|
||||||
; CL = starting sector (1-based)
|
; CL = starting sector (1-based)
|
||||||
; SI = destination offset (Segment:ES already set)
|
|
||||||
; Inputs:
|
; Inputs:
|
||||||
; AL = sector count
|
; AL = sector count
|
||||||
; CH = cylinder
|
; CH = cylinder
|
||||||
; DH = head
|
; DH = head
|
||||||
; CL = sector (1–63, with top 2 bits as high cylinder bits)
|
; CL = sector (1–63, with top 2 bits as high cylinder bits)
|
||||||
; SI = destination offset (segment ES must be set)
|
|
||||||
|
|
||||||
; ----------------------------------------------------------------
|
; ----------------------------------------------------------------
|
||||||
; Convert LBA to CHS
|
; Convert LBA to CHS
|
||||||
@@ -117,36 +125,29 @@ verify_checksum:
|
|||||||
; CL = sector (1-63, top 2 bits are upper cylinder bits)
|
; CL = sector (1-63, top 2 bits are upper cylinder bits)
|
||||||
|
|
||||||
lba_to_chs:
|
lba_to_chs:
|
||||||
pusha
|
; Sector
|
||||||
|
|
||||||
xor dx, dx
|
xor dx, dx
|
||||||
mov bx, [sectors_per_track]
|
mov bx, ax
|
||||||
div bx ; AX = LBA / sectors_per_track, DX = remainder (sector number)
|
div word [sectors_per_track] ; divide lba with max sectors
|
||||||
mov si, ax ; SI = temp quotient (track index)
|
add dl, 1 ; take the remainder, sectors start at 1
|
||||||
mov cx, [heads_per_cylinder]
|
mov cl, dl ; sector is in cl
|
||||||
xor dx, dx
|
|
||||||
div cx ; AX = cylinder, DX = head
|
|
||||||
mov ch, al ; CH = cylinder low byte
|
|
||||||
mov dh, dl ; DH = head
|
|
||||||
|
|
||||||
; Now take sector number from earlier remainder
|
; Head
|
||||||
mov cx, si ; Copy track index to CX to access CL
|
mov ax, bx
|
||||||
and cl, 0x3F ; Mask to 6 bits (sector number)
|
mov dx, 0
|
||||||
inc cl ; Sector numbers are 1-based
|
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
|
||||||
|
|
||||||
; Insert upper 2 bits of cylinder into CL
|
; Cylinder
|
||||||
mov ah, al ; AH = cylinder again
|
mov ch, al ; take the quotient, cylinder is in ch
|
||||||
and ah, 0xC0 ; Get top 2 bits of cylinder
|
|
||||||
or cl, ah ; OR them into sector byte
|
|
||||||
|
|
||||||
popa
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
read_chs:
|
read_chs:
|
||||||
pusha
|
pusha
|
||||||
push dx
|
push dx
|
||||||
|
|
||||||
mov cx, 5
|
|
||||||
.retry:
|
.retry:
|
||||||
mov ah, 0x02 ; BIOS: Read sectors
|
mov ah, 0x02 ; BIOS: Read sectors
|
||||||
mov dl, [bootdev] ; Boot device
|
mov dl, [bootdev] ; Boot device
|
||||||
@@ -262,7 +263,7 @@ switch_to_pm:
|
|||||||
mov eax, cr0
|
mov eax, cr0
|
||||||
or eax, 1
|
or eax, 1
|
||||||
mov cr0, eax
|
mov cr0, eax
|
||||||
jmp 0x08:0x7E00
|
jmp 0x08:0x7E00 ; jump to S2
|
||||||
|
|
||||||
; ----------------------------------------------------------------
|
; ----------------------------------------------------------------
|
||||||
print_string_16:
|
print_string_16:
|
||||||
@@ -284,8 +285,8 @@ halt:
|
|||||||
hlt
|
hlt
|
||||||
|
|
||||||
bootdev db 0
|
bootdev db 0
|
||||||
sectors_per_track dw 63
|
sectors_per_track dw 0
|
||||||
heads_per_cylinder dw 255
|
heads_per_cylinder dw 0
|
||||||
|
|
||||||
times 510 - ($ - $$) db 0
|
times 510 - ($ - $$) db 0
|
||||||
dw 0xAA55
|
dw 0xAA55
|
||||||
26
bootloader/stage2.asm
Normal file
26
bootloader/stage2.asm
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[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
|
||||||
12
bootloader/stage2.ld
Normal file
12
bootloader/stage2.ld
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
SECTIONS {
|
||||||
|
. = 0x7e00;
|
||||||
|
|
||||||
|
.text : { *(.text*) }
|
||||||
|
.rodata : { *(.rodata*) }
|
||||||
|
.data : { *(.data*) }
|
||||||
|
|
||||||
|
.bss : {
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
}
|
||||||
|
}
|
||||||
235
bootloader/stage2_load.c
Normal file
235
bootloader/stage2_load.c
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// ATA IO Ports
|
||||||
|
#define ATA_PRIMARY_DATA 0x1F0
|
||||||
|
#define ATA_PRIMARY_ERR_FEATURES 0x1F1
|
||||||
|
#define ATA_PRIMARY_SEC_COUNT 0x1F2
|
||||||
|
#define ATA_PRIMARY_LBA_LOW 0x1F3
|
||||||
|
#define ATA_PRIMARY_LBA_MID 0x1F4
|
||||||
|
#define ATA_PRIMARY_LBA_HIGH 0x1F5
|
||||||
|
#define ATA_PRIMARY_DRIVE_SEL 0x1F6
|
||||||
|
#define ATA_PRIMARY_COMM_STAT 0x1F7
|
||||||
|
|
||||||
|
// ATA Commands
|
||||||
|
#define ATA_CMD_READ_PIO 0x20
|
||||||
|
#define ATA_CMD_WRITE_PIO 0x30
|
||||||
|
|
||||||
|
// ELF Ident indexes
|
||||||
|
#define EI_NIDENT 16
|
||||||
|
|
||||||
|
// Program header types
|
||||||
|
#define PT_NULL 0
|
||||||
|
#define PT_LOAD 1
|
||||||
|
|
||||||
|
// Disk sector size
|
||||||
|
#define SECTOR_SIZE 512
|
||||||
|
#define PH_PER_SECTOR (SECTOR_SIZE / sizeof(Elf32_Phdr))
|
||||||
|
|
||||||
|
// Kernel start LBA
|
||||||
|
#define KERN_START_SECT 5
|
||||||
|
|
||||||
|
// VGA
|
||||||
|
// Expects bios initialization for text mode (3), buffer at 0xb8000
|
||||||
|
#define VGA_ADDRESS 0xB8000
|
||||||
|
#define VGA_COLS 80
|
||||||
|
#define VGA_ROWS 25
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
static inline uint8_t inb(uint16_t port)
|
||||||
|
{
|
||||||
|
uint8_t ret;
|
||||||
|
__asm__ volatile ("inb %1, %0"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "Nd"(port));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outb(uint16_t port, uint8_t val)
|
||||||
|
{
|
||||||
|
__asm__ volatile ("outb %0, %1"
|
||||||
|
:
|
||||||
|
: "a"(val), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t inw(uint16_t port)
|
||||||
|
{
|
||||||
|
uint16_t ret;
|
||||||
|
__asm__ volatile ("inw %1, %0"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "Nd"(port));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ata_wait_bsy() {
|
||||||
|
while (inb(ATA_PRIMARY_COMM_STAT) & 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ata_wait_drq() {
|
||||||
|
while (!(inb(ATA_PRIMARY_COMM_STAT) & 0x08));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ata_read_sector(void *addr, uint32_t lba) {
|
||||||
|
ata_wait_bsy();
|
||||||
|
|
||||||
|
outb(ATA_PRIMARY_DRIVE_SEL, 0xE0 | ((lba >> 24) & 0x0F));
|
||||||
|
outb(ATA_PRIMARY_SEC_COUNT, 1);
|
||||||
|
outb(ATA_PRIMARY_LBA_LOW, (uint8_t)lba);
|
||||||
|
outb(ATA_PRIMARY_LBA_MID, (uint8_t)(lba >> 8));
|
||||||
|
outb(ATA_PRIMARY_LBA_HIGH, (uint8_t)(lba >> 16));
|
||||||
|
outb(ATA_PRIMARY_COMM_STAT, ATA_CMD_READ_PIO);
|
||||||
|
|
||||||
|
uint16_t* ptr = (uint16_t*)addr;
|
||||||
|
ata_wait_bsy();
|
||||||
|
ata_wait_drq();
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
*ptr++ = inw(ATA_PRIMARY_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ata_read_sectors(uint8_t *addr, uint32_t offset, uint32_t size)
|
||||||
|
{
|
||||||
|
// Reads are offset from the starting sector of the kernel
|
||||||
|
uint32_t lba = KERN_START_SECT + offset / SECTOR_SIZE;
|
||||||
|
uint32_t off = offset % 512;
|
||||||
|
uint8_t data[512];
|
||||||
|
|
||||||
|
while (size > 0) {
|
||||||
|
ata_read_sector(data, lba);
|
||||||
|
|
||||||
|
uint32_t copy = 512 - off;
|
||||||
|
if (copy > size) {
|
||||||
|
copy = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < copy; i++) {
|
||||||
|
addr[i] = data[off + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
addr += copy;
|
||||||
|
size -= copy;
|
||||||
|
lba++;
|
||||||
|
off = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_error(const char *msg)
|
||||||
|
{
|
||||||
|
uint16_t *ptr = (uint16_t *)VGA_ADDRESS;
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
uint16_t val = 0x0f00 | (uint8_t)' ';
|
||||||
|
for (size_t i = 0; i < VGA_COLS * VGA_ROWS; i++) {
|
||||||
|
ptr[i] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print error
|
||||||
|
for (size_t i = 0; msg[i]; i++) {
|
||||||
|
ptr[i] = 0xf00 | (uint8_t)msg[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halt
|
||||||
|
while (1) {
|
||||||
|
__asm__("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load an ELF executable into memory.
|
||||||
|
// NOTE: Only 32-byte program headers are supported.
|
||||||
|
// Returns the entry point to the program.
|
||||||
|
static void *elf_load(const void *data) {
|
||||||
|
const Elf32_Ehdr *header = (const Elf32_Ehdr*)data;
|
||||||
|
|
||||||
|
if (header->e_phentsize != sizeof(Elf32_Phdr)) {
|
||||||
|
// The bootloader only handles 32-byte program header entries
|
||||||
|
on_error("ERROR: Unsupported program header entry size, halting...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer for the program headers
|
||||||
|
uint8_t file_buf[SECTOR_SIZE];
|
||||||
|
|
||||||
|
// Current file offset to the next program header
|
||||||
|
uint32_t file_offset = header->e_phoff;
|
||||||
|
|
||||||
|
for (int i = 0; i < header->e_phnum; i++) {
|
||||||
|
// Check for sector boundary.
|
||||||
|
// Program headers are read in a sector at a time
|
||||||
|
// 512 / 32 = 16 PH per sector
|
||||||
|
if (i % PH_PER_SECTOR == 0) {
|
||||||
|
uint32_t count = (header->e_phnum - i) * sizeof(Elf32_Phdr);
|
||||||
|
if (count > SECTOR_SIZE) {
|
||||||
|
count = SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads
|
||||||
|
ata_read_sectors(file_buf, file_offset, count);
|
||||||
|
file_offset += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PH being processed currently, index mod 16 as headers
|
||||||
|
// are being loaded in sector by sector.
|
||||||
|
const Elf32_Phdr *ph = (const Elf32_Phdr *)file_buf + (i % PH_PER_SECTOR);
|
||||||
|
|
||||||
|
// Discard non-load segments
|
||||||
|
if (ph->p_type != PT_LOAD)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Load in the segment
|
||||||
|
uint32_t offset = ph->p_offset;
|
||||||
|
uint32_t filesz = ph->p_filesz;
|
||||||
|
uint32_t memsz = ph->p_memsz;
|
||||||
|
uint8_t *vaddr = (uint8_t *)ph->p_vaddr;
|
||||||
|
ata_read_sectors(vaddr, offset, filesz);
|
||||||
|
|
||||||
|
// Zero remaining BSS (if any)
|
||||||
|
if (memsz > filesz) {
|
||||||
|
uint8_t* bss_start = vaddr + filesz;
|
||||||
|
for (uint32_t j = 0; j < memsz - filesz; j++) {
|
||||||
|
bss_start[j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the entry point
|
||||||
|
return (void *)header->e_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *load_kernel(void) {
|
||||||
|
// ELF header buffer
|
||||||
|
uint8_t header_buf[SECTOR_SIZE];
|
||||||
|
|
||||||
|
// Read the first sector (contains the ELF header)
|
||||||
|
ata_read_sector(header_buf, KERN_START_SECT);
|
||||||
|
|
||||||
|
// `elf_load()` returns the entry point
|
||||||
|
return elf_load(header_buf);
|
||||||
|
}
|
||||||
169
configure
vendored
Executable file
169
configure
vendored
Executable file
@@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
TARGET="i386-elf"
|
||||||
|
BINUTILS_VERSION="2.45"
|
||||||
|
GCC_VERSION="15.2.0"
|
||||||
|
|
||||||
|
# Paths
|
||||||
|
SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")"
|
||||||
|
SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
|
||||||
|
PREFIX="$SCRIPT_DIR/cross"
|
||||||
|
SRC_DIR="$PREFIX/src"
|
||||||
|
|
||||||
|
BINUTILS_SRC="$SRC_DIR/binutils-$BINUTILS_VERSION"
|
||||||
|
BINUTILS_BUILD="$PREFIX/build-binutils"
|
||||||
|
GCC_SRC="$SRC_DIR/gcc-$GCC_VERSION"
|
||||||
|
GCC_BUILD="$PREFIX/build-gcc"
|
||||||
|
|
||||||
|
# Flags
|
||||||
|
DEBUG=0
|
||||||
|
HELP=0
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
-h|--help)
|
||||||
|
HELP=1
|
||||||
|
;;
|
||||||
|
-d|--debug)
|
||||||
|
DEBUG=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $arg"
|
||||||
|
echo "Use -h or --help for usage information"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Show help
|
||||||
|
if [[ "$HELP" -eq 1 ]]; then
|
||||||
|
cat << EOF
|
||||||
|
Usage: $0 [OPTIONS]
|
||||||
|
|
||||||
|
Build a cross-compiler toolchain for $TARGET.
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-h, --help Show this help message
|
||||||
|
-d, --debug Enable debug mode (set -x)
|
||||||
|
|
||||||
|
This script will:
|
||||||
|
1. Download binutils $BINUTILS_VERSION and GCC $GCC_VERSION
|
||||||
|
2. Build and install them to: $PREFIX
|
||||||
|
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable debug mode
|
||||||
|
if [[ "$DEBUG" -eq 1 ]]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Print configuration
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
=== Build Configuration ===
|
||||||
|
Target : $TARGET
|
||||||
|
Prefix : $PREFIX
|
||||||
|
Binutils : $BINUTILS_VERSION
|
||||||
|
GCC : $GCC_VERSION
|
||||||
|
===========================
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create directory structure
|
||||||
|
echo "Setting up directories..."
|
||||||
|
mkdir -p "$SRC_DIR"
|
||||||
|
|
||||||
|
# Download sources
|
||||||
|
cd "$SRC_DIR"
|
||||||
|
|
||||||
|
if [[ ! -d "$BINUTILS_SRC" ]]; then
|
||||||
|
echo "Downloading binutils $BINUTILS_VERSION..."
|
||||||
|
wget "https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.gz"
|
||||||
|
echo "Extracting binutils..."
|
||||||
|
tar xf "binutils-$BINUTILS_VERSION.tar.gz"
|
||||||
|
rm "binutils-$BINUTILS_VERSION.tar.gz"
|
||||||
|
else
|
||||||
|
echo "Binutils source already exists, skipping download"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "$GCC_SRC" ]]; then
|
||||||
|
echo "Downloading GCC $GCC_VERSION..."
|
||||||
|
wget "https://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.gz"
|
||||||
|
echo "Extracting GCC..."
|
||||||
|
tar xf "gcc-$GCC_VERSION.tar.gz"
|
||||||
|
rm "gcc-$GCC_VERSION.tar.gz"
|
||||||
|
else
|
||||||
|
echo "GCC source already exists, skipping download"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Download GCC prerequisites
|
||||||
|
if [[ ! -d "$GCC_SRC/gmp" ]]; then
|
||||||
|
echo "Downloading GCC prerequisites..."
|
||||||
|
cd "$GCC_SRC"
|
||||||
|
./contrib/download_prerequisites
|
||||||
|
cd "$SRC_DIR"
|
||||||
|
else
|
||||||
|
echo "GCC prerequisites already downloaded, skipping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build binutils
|
||||||
|
if [[ ! -f "$PREFIX/bin/$TARGET-ld" ]]; then
|
||||||
|
echo "Building binutils..."
|
||||||
|
mkdir -p "$BINUTILS_BUILD"
|
||||||
|
cd "$BINUTILS_BUILD"
|
||||||
|
|
||||||
|
"$BINUTILS_SRC/configure" \
|
||||||
|
--target="$TARGET" \
|
||||||
|
--prefix="$PREFIX" \
|
||||||
|
--with-sysroot \
|
||||||
|
--disable-nls \
|
||||||
|
--disable-werror
|
||||||
|
|
||||||
|
make -j"$(nproc)"
|
||||||
|
make install
|
||||||
|
else
|
||||||
|
echo "Binutils already installed, skipping build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build GCC
|
||||||
|
if [[ ! -f "$PREFIX/bin/$TARGET-gcc" ]]; then
|
||||||
|
echo "Building GCC..."
|
||||||
|
mkdir -p "$GCC_BUILD"
|
||||||
|
cd "$GCC_BUILD"
|
||||||
|
|
||||||
|
"$GCC_SRC/configure" \
|
||||||
|
--target="$TARGET" \
|
||||||
|
--prefix="$PREFIX" \
|
||||||
|
--disable-nls \
|
||||||
|
--enable-languages=c \
|
||||||
|
--without-headers
|
||||||
|
|
||||||
|
make all-gcc -j"$(nproc)"
|
||||||
|
make all-target-libgcc -j"$(nproc)"
|
||||||
|
make install-gcc
|
||||||
|
make install-target-libgcc
|
||||||
|
else
|
||||||
|
echo "GCC already installed, skipping build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# Generate .build.env file
|
||||||
|
cat > .build.env << EOF
|
||||||
|
# Generated by configure on $(date)
|
||||||
|
# Source this file to add the cross-compiler toolchain to your PATH
|
||||||
|
export PATH="$PREFIX/bin:\$PATH"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Build Complete ==="
|
||||||
|
echo "Toolchain installed to: $PREFIX"
|
||||||
|
echo ""
|
||||||
|
echo "To use the toolchain, run:"
|
||||||
|
echo " source .build.env"
|
||||||
|
echo "======================"
|
||||||
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
|
||||||
91
kernel/ata.c
Normal file
91
kernel/ata.c
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#include "ata.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "print.h"
|
||||||
|
|
||||||
|
#define ATA_TIMEOUT 100000
|
||||||
|
|
||||||
|
static inline void ata_delay(void) {
|
||||||
|
/* 400ns delay by reading alternate status */
|
||||||
|
inb(ATA_PRIMARY_CTRL);
|
||||||
|
inb(ATA_PRIMARY_CTRL);
|
||||||
|
inb(ATA_PRIMARY_CTRL);
|
||||||
|
inb(ATA_PRIMARY_CTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ata_wait(uint8_t mask) {
|
||||||
|
for (int i = 0; i < ATA_TIMEOUT; i++) {
|
||||||
|
uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS);
|
||||||
|
if (!(status & ATA_SR_BSY) && (status & mask))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ata_init(void) {
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xA0); // master
|
||||||
|
ata_delay();
|
||||||
|
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
|
||||||
|
ata_delay();
|
||||||
|
|
||||||
|
uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS);
|
||||||
|
if (status == 0)
|
||||||
|
return false; // no drive
|
||||||
|
|
||||||
|
if (!ata_wait(ATA_SR_DRQ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint16_t identify[256];
|
||||||
|
for (int i = 0; i < 256; i++)
|
||||||
|
identify[i] = inw(ATA_PRIMARY_IO);
|
||||||
|
|
||||||
|
print_string("[ATA] Primary master detected\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ata_read_sector(uint32_t lba, uint8_t* buffer) {
|
||||||
|
if (!buffer) return false;
|
||||||
|
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F));
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1);
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba));
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
|
||||||
|
|
||||||
|
if (!ata_wait(ATA_SR_DRQ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
uint16_t data = inw(ATA_PRIMARY_IO);
|
||||||
|
buffer[i * 2] = data & 0xFF;
|
||||||
|
buffer[i * 2 + 1] = data >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
ata_delay();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ata_write_sector(uint32_t lba, const uint8_t* buffer) {
|
||||||
|
if (!buffer) return false;
|
||||||
|
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F));
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1);
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba));
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||||
|
outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
|
||||||
|
|
||||||
|
if (!ata_wait(ATA_SR_DRQ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
uint16_t word =
|
||||||
|
buffer[i * 2] |
|
||||||
|
(buffer[i * 2 + 1] << 8);
|
||||||
|
outw(ATA_PRIMARY_IO, word);
|
||||||
|
}
|
||||||
|
|
||||||
|
ata_delay();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
43
kernel/ata.h
Normal file
43
kernel/ata.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef ATA_H
|
||||||
|
#define ATA_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* ATA I/O ports */
|
||||||
|
#define ATA_PRIMARY_IO 0x1F0
|
||||||
|
#define ATA_PRIMARY_CTRL 0x3F6
|
||||||
|
|
||||||
|
/* ATA registers */
|
||||||
|
#define ATA_REG_DATA 0x00
|
||||||
|
#define ATA_REG_ERROR 0x01
|
||||||
|
#define ATA_REG_FEATURES 0x01
|
||||||
|
#define ATA_REG_SECCOUNT0 0x02
|
||||||
|
#define ATA_REG_LBA0 0x03
|
||||||
|
#define ATA_REG_LBA1 0x04
|
||||||
|
#define ATA_REG_LBA2 0x05
|
||||||
|
#define ATA_REG_HDDEVSEL 0x06
|
||||||
|
#define ATA_REG_COMMAND 0x07
|
||||||
|
#define ATA_REG_STATUS 0x07
|
||||||
|
|
||||||
|
/* ATA commands */
|
||||||
|
#define ATA_CMD_READ_PIO 0x20
|
||||||
|
#define ATA_CMD_WRITE_PIO 0x30
|
||||||
|
#define ATA_CMD_IDENTIFY 0xEC
|
||||||
|
|
||||||
|
/* Status flags */
|
||||||
|
#define ATA_SR_BSY 0x80
|
||||||
|
#define ATA_SR_DRDY 0x40
|
||||||
|
#define ATA_SR_DRQ 0x08
|
||||||
|
#define ATA_SR_ERR 0x01
|
||||||
|
|
||||||
|
/* Drive select */
|
||||||
|
#define ATA_MASTER 0x00
|
||||||
|
#define ATA_SLAVE 0x10
|
||||||
|
|
||||||
|
/* Public API */
|
||||||
|
bool ata_init(void);
|
||||||
|
bool ata_read_sector(uint32_t lba, uint8_t* buffer);
|
||||||
|
bool ata_write_sector(uint32_t lba, const uint8_t* buffer);
|
||||||
|
|
||||||
|
#endif
|
||||||
25
kernel/context_switch.asm
Normal file
25
kernel/context_switch.asm
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
|
||||||
92
kernel/cpu.c
92
kernel/cpu.c
@@ -2,36 +2,106 @@
|
|||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "utils.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 leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
|
||||||
__asm__(
|
__asm__(
|
||||||
"cpuid"
|
"cpuid"
|
||||||
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
|
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
|
||||||
: "a"(function)
|
: "a"(leaf)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to print a labeled decimal value
|
||||||
|
void print_val(const char* label, uint32_t val) {
|
||||||
|
char buf[12];
|
||||||
|
utoa(val, buf, 10);
|
||||||
|
terminal_write(label);
|
||||||
|
terminal_write(buf);
|
||||||
|
terminal_write(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safely check if CPUID is supported by attempting to flip bit 21 of EFLAGS
|
||||||
|
int check_cpuid_supported() {
|
||||||
|
uint32_t f1, f2;
|
||||||
|
__asm__ volatile (
|
||||||
|
"pushfl\n\t"
|
||||||
|
"pushfl\n\t"
|
||||||
|
"popl %0\n\t"
|
||||||
|
"movl %0, %1\n\t"
|
||||||
|
"xorl $0x200000, %0\n\t"
|
||||||
|
"pushl %0\n\t"
|
||||||
|
"popfl\n\t"
|
||||||
|
"pushfl\n\t"
|
||||||
|
"popl %0\n\t"
|
||||||
|
"popfl\n\t"
|
||||||
|
: "=&r" (f1), "=&r" (f2));
|
||||||
|
return ((f1 ^ f2) & 0x200000) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
void identify_cpu() {
|
void identify_cpu() {
|
||||||
|
if (!check_cpuid_supported()) {
|
||||||
|
terminal_write("CPUID not supported. Likely a 386 or early 486.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t eax, ebx, ecx, edx;
|
uint32_t eax, ebx, ecx, edx;
|
||||||
char vendor[13];
|
char vendor[13];
|
||||||
|
|
||||||
|
// Leaf 0: Vendor String & Max Leaf
|
||||||
cpuid(0, &eax, &ebx, &ecx, &edx);
|
cpuid(0, &eax, &ebx, &ecx, &edx);
|
||||||
|
uint32_t max_leaf = eax;
|
||||||
*(uint32_t *)&vendor[0] = ebx;
|
*(uint32_t *)&vendor[0] = ebx;
|
||||||
*(uint32_t *)&vendor[4] = edx;
|
*(uint32_t *)&vendor[4] = edx;
|
||||||
*(uint32_t *)&vendor[8] = ecx;
|
*(uint32_t *)&vendor[8] = ecx;
|
||||||
vendor[12] = '\0';
|
vendor[12] = '\0';
|
||||||
|
|
||||||
terminal_write("CPU Vendor: ");
|
terminal_write("Vendor: ");
|
||||||
terminal_write(vendor);
|
terminal_write(vendor);
|
||||||
terminal_write("\n");
|
terminal_write("\n");
|
||||||
|
|
||||||
serial_write("CPU Vendor: ");
|
// Leaf 1: Family, Model, Stepping
|
||||||
serial_write(vendor);
|
if (max_leaf >= 1) {
|
||||||
serial_write("\n");
|
cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||||
|
|
||||||
terminal_write("CPUID max leaf: ");
|
uint32_t stepping = eax & 0xF;
|
||||||
print_hex(eax, false, false); // You must implement this (see below)
|
uint32_t model = (eax >> 4) & 0xF;
|
||||||
terminal_write("\n");
|
uint32_t family = (eax >> 8) & 0xF;
|
||||||
|
uint32_t type = (eax >> 12) & 0x3;
|
||||||
|
|
||||||
|
// Handle Extended Family/Model (Required for Pentium 4 and newer)
|
||||||
|
if (family == 0xF) {
|
||||||
|
family += (eax >> 20) & 0xFF;
|
||||||
|
model += ((eax >> 16) & 0xF) << 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_val("Family:", family);
|
||||||
|
print_val("Model:", model);
|
||||||
|
print_val("Step:", stepping);
|
||||||
|
terminal_write("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leaf 2: Cache Descriptors
|
||||||
|
if (max_leaf >= 2) {
|
||||||
|
cpuid(2, &eax, &ebx, &ecx, &edx);
|
||||||
|
|
||||||
|
terminal_write("Cache Descriptors: ");
|
||||||
|
// Note: Leaf 2 returns a list of 1-byte descriptors in the registers.
|
||||||
|
// We look for common Intel ones:
|
||||||
|
uint32_t regs[4] = {eax, ebx, ecx, edx};
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (regs[i] & 0x80000000) continue; // Reserved bit
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
uint8_t desc = (regs[i] >> (j * 8)) & 0xFF;
|
||||||
|
if (desc == 0) continue;
|
||||||
|
|
||||||
|
// Example decoding for specific chips you mentioned:
|
||||||
|
if (desc == 0x06) terminal_write("8KB L1 I-Cache ");
|
||||||
|
if (desc == 0x0A) terminal_write("8KB L1 D-Cache ");
|
||||||
|
if (desc == 0x41) terminal_write("128KB L2 ");
|
||||||
|
if (desc == 0x43) terminal_write("512KB L2 ");
|
||||||
|
if (desc == 0x2C) terminal_write("32KB L1 D-Cache ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
terminal_write("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
kernel/cpu.h
36
kernel/cpu.h
@@ -2,8 +2,42 @@
|
|||||||
#define CPU_H
|
#define CPU_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
void cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
|
// Specific Intel Model Definitions for your targets
|
||||||
|
#define INTEL_FAM4_486_DX 0x00 // Also 0x01
|
||||||
|
#define INTEL_FAM4_486_SX 0x02
|
||||||
|
#define INTEL_FAM4_486_DX2 0x03
|
||||||
|
#define INTEL_FAM4_486_DX4 0x08
|
||||||
|
#define INTEL_FAM5_PENTIUM 0x01 // P5
|
||||||
|
#define INTEL_FAM5_PENTIUM_MMX 0x04 // P55C
|
||||||
|
#define INTEL_FAM6_PENTIUM_PRO 0x01 // P6
|
||||||
|
#define INTEL_FAM6_PENTIUM_II 0x05 // Deschutes
|
||||||
|
#define INTEL_FAM6_PENTIUM_III 0x07 // Katmai/Coppermine
|
||||||
|
#define INTEL_FAM15_P4_WILLY 0x00 // Willamette
|
||||||
|
#define INTEL_FAM15_P4_NORTH 0x02 // Northwood
|
||||||
|
#define INTEL_FAM15_P4_PRES 0x03 // Prescott
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char vendor[13];
|
||||||
|
uint32_t family;
|
||||||
|
uint32_t model;
|
||||||
|
uint32_t stepping;
|
||||||
|
uint32_t type;
|
||||||
|
uint32_t max_leaf;
|
||||||
|
|
||||||
|
// Feature flags (optional, but very helpful later)
|
||||||
|
bool has_fpu;
|
||||||
|
bool has_mmx;
|
||||||
|
bool has_sse;
|
||||||
|
} cpu_info_t;
|
||||||
|
|
||||||
|
// Function Prototypes
|
||||||
|
void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
|
||||||
|
bool cpu_check_cpuid_support(void);
|
||||||
void identify_cpu(void);
|
void identify_cpu(void);
|
||||||
|
|
||||||
|
// Helper to get the current CPU info after identification
|
||||||
|
cpu_info_t* cpu_get_info(void);
|
||||||
|
|
||||||
#endif // CPU_H
|
#endif // CPU_H
|
||||||
|
|||||||
193
kernel/fat12.c
193
kernel/fat12.c
@@ -1,5 +1,196 @@
|
|||||||
#include "fat12.h"
|
#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]) {
|
||||||
|
// Correct way to return the difference:
|
||||||
|
// If p1[i] > p2[i], returns positive.
|
||||||
|
// If p1[i] < p2[i], returns negative.
|
||||||
|
return (int)p1[i] - (int)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() {
|
void fat12_init() {
|
||||||
// Filesystem initialization code
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int disk_read_sector(uint32_t lba, uint8_t *buffer) {
|
||||||
|
// For now, do nothing and return success
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,67 @@
|
|||||||
#ifndef FAT12_H
|
#ifndef FAT12_H
|
||||||
#define FAT12_H
|
#define FAT12_H
|
||||||
|
|
||||||
#include <stdint.h> /* Include standard integer types */
|
#include <stdint.h>
|
||||||
#include <stdio.h> /* Include standard I/O library */
|
|
||||||
#include <stdlib.h> /* Include standard library */
|
|
||||||
|
|
||||||
#define FAT12_SECTOR_SIZE 512 /* Sector size for FAT12 */
|
// --- Configuration ---
|
||||||
#define FAT12_MAX_FILES 128 /* Maximum number of files in root directory */
|
#define FAT12_SECTOR_SIZE 512
|
||||||
#define FAT12_ROOT_DIR_SECTORS 1 /* Number of sectors for root directory */
|
|
||||||
|
|
||||||
|
// --- On-Disk Structures (Must be Packed) ---
|
||||||
|
|
||||||
|
// BIOS Parameter Block (Start of Boot Sector)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t jump[3]; /* Jump instruction for boot */
|
uint8_t jump[3];
|
||||||
char oem[8]; /* OEM name */
|
char oem[8];
|
||||||
uint16_t bytes_per_sector; /* Bytes per sector */
|
uint16_t bytes_per_sector; // 512
|
||||||
uint8_t sectors_per_cluster; /* Sectors per cluster */
|
uint8_t sectors_per_cluster; // 1
|
||||||
uint16_t reserved_sectors; /* Reserved sectors count */
|
uint16_t reserved_sectors; // 1 (Boot sector)
|
||||||
uint8_t num_fats; /* Number of FATs */
|
uint8_t fat_count; // 2
|
||||||
uint16_t max_root_dir_entries; /* Max entries in root directory */
|
uint16_t dir_entries_count; // 224
|
||||||
uint16_t total_sectors; /* Total sectors */
|
uint16_t total_sectors; // 2880
|
||||||
uint8_t media_descriptor; /* Media descriptor */
|
uint8_t media_descriptor; // 0xF0
|
||||||
uint16_t fat_size; /* Size of each FAT */
|
uint16_t sectors_per_fat; // 9
|
||||||
uint16_t sectors_per_track; /* Sectors per track */
|
uint16_t sectors_per_track; // 18
|
||||||
uint16_t num_heads; /* Number of heads */
|
uint16_t heads; // 2
|
||||||
uint32_t hidden_sectors; /* Hidden sectors count */
|
uint32_t hidden_sectors;
|
||||||
uint32_t total_sectors_large; /* Total sectors for large disks */
|
uint32_t total_sectors_large;
|
||||||
} __attribute__((packed)) FAT12_BootSector; /* Packed structure for boot sector */
|
} __attribute__((packed)) fat12_bpb_t;
|
||||||
|
|
||||||
|
// Directory Entry (32 bytes)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char name[11]; /* File name (8.3 format) */
|
char filename[8];
|
||||||
uint8_t attr; /* File attributes */
|
char ext[3];
|
||||||
uint16_t reserved; /* Reserved */
|
uint8_t attributes;
|
||||||
uint16_t time; /* Time of last write */
|
uint8_t reserved;
|
||||||
uint16_t date; /* Date of last write */
|
uint8_t creation_ms;
|
||||||
uint16_t start_cluster; /* Starting cluster number */
|
uint16_t creation_time;
|
||||||
uint32_t file_size; /* File size in bytes */
|
uint16_t creation_date;
|
||||||
} __attribute__((packed)) FAT12_DirEntry; /* Directory entry structure */
|
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;
|
||||||
|
|
||||||
void initialize_fat12(const char *disk_image); /* Function to initialize FAT12 */
|
// --- Kernel File Handle ---
|
||||||
void read_fat12(const char *disk_image); /* Function to read FAT12 */
|
// This is what your kernel uses to track an open file
|
||||||
void write_fat12(const char *disk_image); /* Function to write FAT12 */
|
typedef struct {
|
||||||
void list_files(const char *disk_image); /* Function to list files in root directory */
|
char name[11];
|
||||||
void read_file(const char *disk_image, const char *filename); /* Function to read a file */
|
uint32_t size;
|
||||||
void write_file(const char *disk_image, const char *filename, const uint8_t *data, size_t size); /* Function to write a file */
|
uint16_t start_cluster;
|
||||||
|
uint16_t current_cluster;
|
||||||
|
uint32_t current_sector_in_cluster;
|
||||||
|
uint32_t bytes_read;
|
||||||
|
} file_t;
|
||||||
|
|
||||||
#endif
|
// --- Public API ---
|
||||||
/* FAT12_H */
|
|
||||||
|
// You must implement this in your disk driver (e.g., floppy.c)
|
||||||
|
// Returns 0 on success, non-zero on error.
|
||||||
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
20
kernel/io.h
20
kernel/io.h
@@ -13,4 +13,24 @@ static inline uint8_t inb(uint16_t port) {
|
|||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
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 <stdint.h>
|
||||||
|
|
||||||
#endif // IRQ_H
|
void irq_remap(void);
|
||||||
|
void irq_install(void);
|
||||||
|
void irq_handler(uint32_t int_num);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "isr.h"
|
#include "isr.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "print.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: ");
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -21,39 +25,41 @@ static const char scancode_map[128] = {
|
|||||||
|
|
||||||
// Interrupt handler for IRQ1
|
// Interrupt handler for IRQ1
|
||||||
void keyboard_callback(void) {
|
void keyboard_callback(void) {
|
||||||
uint8_t scancode = inb(0x60);
|
uint8_t scancode = inb(KEYBOARD_DATA_PORT);
|
||||||
|
|
||||||
// Only handle key press (ignore key release)
|
if (scancode & 0x80) return; // Ignore key release
|
||||||
if (!(scancode & 0x80)) {
|
|
||||||
char c = scancode_map[scancode];
|
|
||||||
if (c && buffer_index < sizeof(key_buffer) - 1) {
|
|
||||||
key_buffer[buffer_index++] = c;
|
|
||||||
terminal_putchar(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send End of Interrupt (EOI) to the PIC
|
char c = scancode_map[scancode];
|
||||||
outb(0x20, 0x20);
|
if (!c) return;
|
||||||
|
|
||||||
|
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() {
|
||||||
register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21)
|
register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocking read (returns one char)
|
// Blocking read (returns one char)
|
||||||
char keyboard_get_char() {
|
char keyboard_get_char(void) {
|
||||||
while (buffer_index == 0); // Busy wait
|
while (buffer_count == 0) {
|
||||||
|
__asm__ __volatile__("hlt"); // Better than busy loop
|
||||||
|
}
|
||||||
|
|
||||||
char c;
|
char c;
|
||||||
__asm__ __volatile__("cli");
|
__asm__ __volatile__("cli");
|
||||||
c = key_buffer[0];
|
c = key_buffer[buffer_tail];
|
||||||
for (uint8_t i = 1; i < buffer_index; i++) {
|
buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE;
|
||||||
key_buffer[i - 1] = key_buffer[i];
|
buffer_count--;
|
||||||
}
|
|
||||||
buffer_index--;
|
|
||||||
__asm__ __volatile__("sti");
|
__asm__ __volatile__("sti");
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
|
#include "irq.h"
|
||||||
|
|
||||||
#define LPT1 0x378
|
#define LPT1 0x378
|
||||||
|
|
||||||
@@ -41,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");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "kmalloc.h"
|
#include "kmalloc.h"
|
||||||
#include "terminal.h" // Optional: for debug output
|
#include "terminal.h" // Optional: for debug output
|
||||||
|
|
||||||
#define HEAP_END 0xC0100000
|
#define HEAP_END 0xC0500000
|
||||||
|
|
||||||
static uint32_t current_heap = 0;
|
static uint32_t current_heap = 0;
|
||||||
|
|
||||||
|
|||||||
38
kernel/linker.ld
Normal file
38
kernel/linker.ld
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
ENTRY(kmain)
|
||||||
|
|
||||||
|
PHDRS {
|
||||||
|
text PT_LOAD FLAGS(5); /* Read + Execute */
|
||||||
|
rodata PT_LOAD FLAGS(4); /* Read only */
|
||||||
|
data PT_LOAD FLAGS(6); /* Read + Write */
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
. = 1M;
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
*(.text*)
|
||||||
|
} :text
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
*(.rodata*)
|
||||||
|
} :rodata
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
*(.data*)
|
||||||
|
} :data
|
||||||
|
|
||||||
|
.bss : {
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
} :data
|
||||||
|
|
||||||
|
.stack (NOLOAD) : {
|
||||||
|
. = ALIGN(4);
|
||||||
|
. = . + 0x1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heap (NOLOAD) : {
|
||||||
|
. = ALIGN(4);
|
||||||
|
. = . + 0x10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
kernel/memory.c
Normal file
17
kernel/memory.c
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
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 */
|
||||||
@@ -1,21 +1,17 @@
|
|||||||
#include "paging.h"
|
|
||||||
#include "io.h"
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <string.h>
|
||||||
|
#include "io.h"
|
||||||
|
#include "paging.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
|
|
||||||
|
|
||||||
// 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) {
|
||||||
for (int i = 0; i < PAGE_DIRECTORY_SIZE; i++) {
|
// Set first PDE
|
||||||
dir[i].present = 0;
|
|
||||||
}
|
|
||||||
dir[0].present = 1;
|
dir[0].present = 1;
|
||||||
dir[0].rw = 1;
|
dir[0].rw = 1;
|
||||||
dir[0].user = 0;
|
dir[0].addr = (uint32_t)page_table >> 12;
|
||||||
dir[0].frame = (uint32_t)page_table >> 12;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to set up the page table entry
|
// Helper function to set up the page table entry
|
||||||
@@ -23,12 +19,8 @@ void set_page_table(page_table_entry_t *table) {
|
|||||||
for (int i = 0; i < PAGE_TABLE_SIZE; i++) {
|
for (int i = 0; i < PAGE_TABLE_SIZE; i++) {
|
||||||
// Set up page table entries with identity mapping
|
// Set up page table entries with identity mapping
|
||||||
table[i].present = 1;
|
table[i].present = 1;
|
||||||
table[i].rw = 1; // Read/Write
|
table[i].rw = 1; // Read/Write
|
||||||
table[i].user = 0; // Kernel mode
|
table[i].addr = i; // Identity mapping
|
||||||
table[i].write_through = 0;
|
|
||||||
table[i].cache_disabled = 0;
|
|
||||||
table[i].accessed = 0;
|
|
||||||
table[i].frame = i; // Identity mapping
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,26 +39,13 @@ void enable_paging() {
|
|||||||
|
|
||||||
// Initialize paging: set up the page directory and enable paging
|
// Initialize paging: set up the page directory and enable paging
|
||||||
void paging_init() {
|
void paging_init() {
|
||||||
|
// Zero out the tables
|
||||||
|
memset(page_directory, 0x00, PAGE_DIRECTORY_SIZE * sizeof *page_directory);
|
||||||
|
memset(page_table, 0x00, PAGE_TABLE_SIZE * sizeof *page_table);
|
||||||
|
|
||||||
// Set up identity-mapped page directory + table
|
// Set up identity-mapped page directory + table
|
||||||
set_page_directory(page_directory);
|
set_page_directory(page_directory);
|
||||||
set_page_table(page_table);
|
set_page_table(page_table);
|
||||||
|
|
||||||
// === Set up heap mapping at 0xC0100000 ===
|
|
||||||
for (int i = 0; i < PAGE_TABLE_SIZE; i++) {
|
|
||||||
heap_page_table[i].present = 1;
|
|
||||||
heap_page_table[i].rw = 1;
|
|
||||||
heap_page_table[i].user = 0;
|
|
||||||
heap_page_table[i].write_through = 0;
|
|
||||||
heap_page_table[i].cache_disabled = 0;
|
|
||||||
heap_page_table[i].accessed = 0;
|
|
||||||
heap_page_table[i].frame = (256 + i); // Start physical heap at 1MB (256*4KB = 1MB)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index 772 = 0xC0100000 / 4MB
|
|
||||||
page_directory[772].present = 1;
|
|
||||||
page_directory[772].rw = 1;
|
|
||||||
page_directory[772].user = 0;
|
|
||||||
page_directory[772].frame = (uint32_t)heap_page_table >> 12;
|
|
||||||
|
|
||||||
enable_paging();
|
enable_paging();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,31 +10,31 @@
|
|||||||
|
|
||||||
// Page Directory and Page Table structure
|
// Page Directory and Page Table structure
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t present : 1; // Present bit (1: page is present in memory)
|
uint32_t present : 1; // Present bit (1: page is present in memory)
|
||||||
uint32_t rw : 1; // Read-Write bit (1: page is read-write)
|
uint32_t rw : 1; // Read-Write bit (1: page is read-write)
|
||||||
uint32_t user : 1; // User-supervisor bit (1: user mode access)
|
uint32_t user : 1; // User-supervisor bit (1: user mode access)
|
||||||
uint32_t write_through : 1; // Write-through cache
|
uint32_t write_through : 1; // Write-through cache
|
||||||
uint32_t cache_disabled : 1; // Cache disabled
|
uint32_t cache_disabled : 1; // Cache disabled
|
||||||
uint32_t accessed : 1; // Accessed bit
|
uint32_t accessed : 1; // Accessed bit
|
||||||
uint32_t reserved : 1; // Reserved bit
|
uint32_t dirty : 1; // Dirty bit
|
||||||
uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB)
|
uint32_t attribute : 1; // Page size (0: 4KB, 1: 4MB)
|
||||||
uint32_t global : 1; // Global page (can be used across different processes)
|
uint32_t global : 1; // Global page (can be used across different processes)
|
||||||
uint32_t available : 3; // Available bits for the system
|
uint32_t reserved : 3; // Unused
|
||||||
uint32_t frame : 20; // Frame address (physical address)
|
uint32_t addr : 20; // Page frame address (physical address)
|
||||||
} __attribute__((packed)) page_table_entry_t;
|
} __attribute__((packed)) page_table_entry_t;
|
||||||
|
|
||||||
// Define page directory entry
|
// Define page directory entry
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t present : 1;
|
uint32_t present : 1; // Present bit (1: PTE is present in memory)
|
||||||
uint32_t rw : 1;
|
uint32_t rw : 1; // Read-Write bit (1: pages are read-write)
|
||||||
uint32_t user : 1;
|
uint32_t user : 1; // User-supervisor bit (1: user mode access)
|
||||||
uint32_t write_through : 1;
|
uint32_t write_through : 1; // Write-through cache
|
||||||
uint32_t cache_disabled : 1;
|
uint32_t cache_disabled : 1; // Cache disabled
|
||||||
uint32_t accessed : 1;
|
uint32_t accessed : 1; // Accessed bit
|
||||||
uint32_t reserved : 1;
|
uint32_t available : 1; // Unused
|
||||||
uint32_t zero : 5; // Must be zero for page directory
|
uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB)
|
||||||
uint32_t reserved_2 : 7; // Reserved bits
|
uint32_t available_2 : 4; // Unused
|
||||||
uint32_t frame : 20; // Frame address of the page table
|
uint32_t addr : 20; // Page table address
|
||||||
} __attribute__((packed)) page_directory_entry_t;
|
} __attribute__((packed)) page_directory_entry_t;
|
||||||
|
|
||||||
extern page_directory_entry_t *page_directory;
|
extern page_directory_entry_t *page_directory;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef PRINT_H
|
#ifndef PRINT_H
|
||||||
#define PRINT_H
|
#define PRINT_H
|
||||||
|
|
||||||
#include "types.h"
|
#include <stdint.h>
|
||||||
|
|
||||||
void print_string(const char *str);
|
void print_string(const char *str);
|
||||||
void my_printf(const char *format, ...);
|
void my_printf(const char *format, ...);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
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,9 +1,8 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
#include "threading.h"
|
#include "threading.h"
|
||||||
#include "types.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define MAX_THREADS 16 // Maximum number of threads
|
#define MAX_THREADS 16 // Maximum number of threads
|
||||||
#define THREAD_STACK_SIZE 8192 // Stack size for each thread
|
#define THREAD_STACK_SIZE 8192 // Stack size for each thread
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
#include "types.h"
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -76,20 +76,3 @@ char* utoa(unsigned int value, char* str, int base) {
|
|||||||
reverse(str, i);
|
reverse(str, i);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
int memcmp(const void *ptr1, const void *ptr2, size_t num) {
|
|
||||||
const uint8_t *p1 = ptr1, *p2 = ptr2;
|
|
||||||
for (size_t i = 0; i < num; i++) {
|
|
||||||
if (p1[i] != p2[i]) {
|
|
||||||
return p1[i] < p2[i] ? -1 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *memset(void *dest, int value, size_t len) {
|
|
||||||
unsigned char *ptr = (unsigned char *)dest;
|
|
||||||
while (len-- > 0)
|
|
||||||
*ptr++ = (unsigned char)value;
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef UTILS_H
|
#ifndef UTILS_H
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
|
|
||||||
#include "types.h"
|
#include <stddef.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,7 +9,4 @@ 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);
|
||||||
|
|
||||||
int memcmp(const void *ptr1, const void *ptr2, size_t num);
|
|
||||||
void *memset(void *dest, int value, size_t len);
|
|
||||||
|
|
||||||
#endif // UTILS_H
|
#endif // UTILS_H
|
||||||
|
|||||||
14
klibc/include/stdarg.h
Normal file
14
klibc/include/stdarg.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef CLASSICOS_KLIBC_STDARG_H
|
||||||
|
#define CLASSICOS_KLIBC_STDARG_H
|
||||||
|
|
||||||
|
typedef __builtin_va_list va_list;
|
||||||
|
|
||||||
|
#ifndef va_start
|
||||||
|
#define va_start(ap, param) __builtin_va_start(ap, param)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define va_end(ap) __builtin_va_end(ap)
|
||||||
|
#define va_arg(ap, type) __builtin_va_arg(ap, type)
|
||||||
|
#define va_copy(dest, src) __builtin_va_copy(dest, src)
|
||||||
|
|
||||||
|
#endif // CLASSICOS_KLIBC_STDARG_H
|
||||||
12
klibc/include/stdbool.h
Normal file
12
klibc/include/stdbool.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef CLASSICOS_KLIBC_STDBOOL_H
|
||||||
|
#define CLASSICOS_KLIBC_STDBOOL_H
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#define bool _Bool
|
||||||
|
#define true 1
|
||||||
|
#define false 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __bool_true_false_are_defined 1
|
||||||
|
|
||||||
|
#endif // CLASSICOS_KLIBC_STDBOOL_H
|
||||||
10
klibc/include/stddef.h
Normal file
10
klibc/include/stddef.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef CLASSICOS_KLIBC_STDDEF_H
|
||||||
|
#define CLASSICOS_KLIBC_STDDEF_H
|
||||||
|
|
||||||
|
typedef __SIZE_TYPE__ size_t;
|
||||||
|
typedef __PTRDIFF_TYPE__ ptrdiff_t;
|
||||||
|
|
||||||
|
#undef NULL
|
||||||
|
#define NULL ((void*)0)
|
||||||
|
|
||||||
|
#endif // CLASSICOS_KLIBC_STDDEF_H
|
||||||
16
klibc/include/stdint.h
Normal file
16
klibc/include/stdint.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef CLASSICOS_KLIBC_STDINT_H
|
||||||
|
#define CLASSICOS_KLIBC_STDINT_H
|
||||||
|
|
||||||
|
typedef signed char int8_t;
|
||||||
|
typedef short int int16_t;
|
||||||
|
typedef int int32_t;
|
||||||
|
typedef long long int int64_t;
|
||||||
|
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned short int uint16_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
typedef unsigned long long int uint64_t;
|
||||||
|
|
||||||
|
typedef unsigned int uintptr_t;
|
||||||
|
|
||||||
|
#endif // CLASSICOS_KLIBC_STDINT_H
|
||||||
4
klibc/include/stdio.h
Normal file
4
klibc/include/stdio.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#ifndef CLASSICOS_KLIBC_STDIO_H
|
||||||
|
#define CLASSICOS_KLIBC_STDIO_H
|
||||||
|
|
||||||
|
#endif // CLASSICOS_KLIBC_STDIO_H
|
||||||
4
klibc/include/stdlib.h
Normal file
4
klibc/include/stdlib.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#ifndef CLASSICOS_KLIBC_STDLIB_H
|
||||||
|
#define CLASSICOS_KLIBC_STDLIB_H
|
||||||
|
|
||||||
|
#endif // CLASSICOS_KLIBC_STDLIB_H
|
||||||
14
klibc/include/string.h
Normal file
14
klibc/include/string.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef CLASSICOS_KLIBC_STRING_H
|
||||||
|
#define CLASSICOS_KLIBC_STRING_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
extern int memcmp(const void* s1, const void* s2, size_t n);
|
||||||
|
extern void* memmove(void* dst, const void* src, size_t n);
|
||||||
|
extern void* memcpy(void* dst, const void* src, size_t n);
|
||||||
|
extern void* memset(void* dst, int c, size_t n);
|
||||||
|
|
||||||
|
extern size_t strlen(const char* s);
|
||||||
|
extern int strcmp(const char* s1, const char* s2);
|
||||||
|
|
||||||
|
#endif // CLASSICOS_KLIBC_STRING_H
|
||||||
107
klibc/src/string.c
Normal file
107
klibc/src/string.c
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int memcmp(const void* s1, const void* s2, size_t n) {
|
||||||
|
const unsigned char* c1 = s1;
|
||||||
|
const unsigned char* c2 = s2;
|
||||||
|
int d = 0;
|
||||||
|
|
||||||
|
while (n--) {
|
||||||
|
d = (int)*c1++ - (int)*c2++;
|
||||||
|
if (d) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memmove(void* dst, const void* src, size_t n) {
|
||||||
|
const char* p = src;
|
||||||
|
char* q = dst;
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
if (q < p) {
|
||||||
|
__asm__ volatile("cld; rep; movsb" : "+c"(n), "+S"(p), "+D"(q));
|
||||||
|
} else {
|
||||||
|
p += (n - 1);
|
||||||
|
q += (n - 1);
|
||||||
|
__asm__ volatile("std; rep; movsb; cld" : "+c"(n), "+S"(p), "+D"(q));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (q < p) {
|
||||||
|
while (n--) {
|
||||||
|
*q++ = *p++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p += n;
|
||||||
|
q += n;
|
||||||
|
while (n--) {
|
||||||
|
*--q = *--p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memcpy(void* dst, const void* src, size_t n) {
|
||||||
|
const char* p = src;
|
||||||
|
char* q = dst;
|
||||||
|
#if defined(__i386__)
|
||||||
|
size_t nl = n >> 2;
|
||||||
|
__asm__ volatile("cld ; rep ; movsl ; movl %3,%0 ; rep ; movsb"
|
||||||
|
: "+c"(nl), "+S"(p), "+D"(q)
|
||||||
|
: "r"(n & 3));
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
size_t nq = n >> 3;
|
||||||
|
__asm__ volatile("cld ; rep ; movsq ; movl %3,%%ecx ; rep ; movsb"
|
||||||
|
: "+c"(nq), "+S"(p), "+D"(q)
|
||||||
|
: "r"((uint32_t)(n & 7)));
|
||||||
|
#else
|
||||||
|
while (n--) {
|
||||||
|
*q++ = *p++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memset(void* dst, int c, size_t n) {
|
||||||
|
char* q = dst;
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
size_t nl = n >> 2;
|
||||||
|
__asm__ volatile("cld ; rep ; stosl ; movl %3,%0 ; rep ; stosb"
|
||||||
|
: "+c"(nl), "+D"(q)
|
||||||
|
: "a"((unsigned char)c * 0x01010101U), "r"(n & 3));
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
size_t nq = n >> 3;
|
||||||
|
__asm__ volatile("cld ; rep ; stosq ; movl %3,%%ecx ; rep ; stosb"
|
||||||
|
: "+c"(nq), "+D"(q)
|
||||||
|
: "a"((unsigned char)c * 0x0101010101010101U),
|
||||||
|
"r"((uint32_t)n & 7));
|
||||||
|
#else
|
||||||
|
while (n--) {
|
||||||
|
*q++ = c;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strlen(const char* s) {
|
||||||
|
const char* ss = s;
|
||||||
|
while (*ss) ss++;
|
||||||
|
return ss - s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strcmp(const char* s1, const char* s2) {
|
||||||
|
const unsigned char* c1 = (const unsigned char*)s1;
|
||||||
|
const unsigned char* c2 = (const unsigned char*)s2;
|
||||||
|
unsigned char ch;
|
||||||
|
int d = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
d = (int)(ch = *c1++) - (int)*c2++;
|
||||||
|
if (d || !ch) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user