mirror of
https://github.com/gbowne1/ClassicOS.git
synced 2025-10-13 13:15:07 -07:00
Compare commits
13 Commits
ecfa54e225
...
main
Author | SHA1 | Date | |
---|---|---|---|
dd68fd805f | |||
16309bc306 | |||
4a189f482f | |||
267130281a | |||
e1e30b511a | |||
109e554524 | |||
69762b6650 | |||
49361a98be | |||
50efcc13fe | |||
a9f2826014 | |||
512bd49ff7 | |||
799f744f47 | |||
10b8fdc33f |
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files.associations": {
|
||||
".fantomasignore": "ignore",
|
||||
"stddef.h": "c",
|
||||
"io.h": "c"
|
||||
}
|
||||
}
|
@@ -48,4 +48,7 @@ Clone and build:
|
||||
git clone https://github.com/gbowne1/ClassicOS.git
|
||||
cd ClassicOS
|
||||
make
|
||||
```
|
||||
```
|
||||
|
||||
build kernel
|
||||
for %f in (*.c) do gcc -m32 -O0 -Wall -Wextra -Werror -pedantic -ffreestanding -nostdlib -fno-pic -fno-stack-protector -fno-pie -march=i386 -mtune=i386 -c "%f" -o "%f.o"
|
||||
|
@@ -1,67 +1,292 @@
|
||||
; ==============================================================================
|
||||
; boot.asm - First Stage Bootloader (CHS Based)
|
||||
; ==============================================================================
|
||||
|
||||
[BITS 16]
|
||||
[ORG 0x7C00]
|
||||
|
||||
start:
|
||||
cli
|
||||
cli ; Disable interrupts
|
||||
|
||||
mov [bootdev], dl ; Save boot device number (from BIOS in DL)
|
||||
|
||||
; Setup stack safely below EBDA area (choose 0x0000:0x7A00)
|
||||
xor ax, ax ; AX = 0
|
||||
mov ss, ax ; Stack segment = 0x0000
|
||||
mov sp, 0x7A00 ; Stack offset = 0x7A00
|
||||
|
||||
; Initialize DS, ES for zero-based segments
|
||||
xor ax, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov ss, ax
|
||||
mov sp, 0x7C00
|
||||
|
||||
; Load second-stage bootloader (boot1.asm) to 0x7E00
|
||||
mov ax, 1 ; LBA of boot1.asm (starts at sector 1)
|
||||
call lba_to_chs
|
||||
mov si, 0x7E00
|
||||
mov al, 4 ; Number of sectors to read
|
||||
call read_chs
|
||||
|
||||
; Load kernel to 0x100000 (1 MB)
|
||||
mov si, 0x0000 ; Destination offset
|
||||
mov ax, 0x1000 ; ES = 0x1000 (0x1000:0x0000 = 1 MB)
|
||||
mov es, ax
|
||||
xor bx, bx
|
||||
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
|
||||
call read_chs
|
||||
jc disk_error
|
||||
|
||||
; Memory Validation: Verify checksum of second stage bootloader
|
||||
mov si, 0x7E00 ; Start of second stage
|
||||
mov cx, 512 * 4 ; Size in bytes (adjust if more sectors loaded)
|
||||
call verify_checksum
|
||||
jc disk_error ; Jump if checksum fails
|
||||
|
||||
; Enable A20 line
|
||||
call enable_a20
|
||||
jc a20_error ; Jump if A20 enable fails
|
||||
|
||||
; Setup Global Descriptor Table
|
||||
call setup_gdt
|
||||
|
||||
; Switch to protected mode and jump to second stage at 0x08:0x7E00
|
||||
call switch_to_pm
|
||||
|
||||
; ----------------------
|
||||
; A20 Gate Enable (Fast method)
|
||||
enable_a20:
|
||||
in al, 0x92
|
||||
or al, 0x02
|
||||
out 0x92, al
|
||||
disk_error:
|
||||
mov si, disk_error_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
a20_error:
|
||||
mov si, a20_error_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
; Verify Checksum Routine
|
||||
; Uses SI = start address, CX = byte count
|
||||
; Simple XOR checksum over bytes, expects result 0
|
||||
|
||||
verify_checksum:
|
||||
push ax
|
||||
push bx
|
||||
push di
|
||||
mov di, si
|
||||
xor al, al
|
||||
xor bx, bx
|
||||
|
||||
.verify_loop:
|
||||
lodsb
|
||||
xor bl, al
|
||||
loop .verify_loop
|
||||
|
||||
test bl, bl
|
||||
jz .checksum_ok
|
||||
stc ; Set carry on checksum error
|
||||
jmp .done
|
||||
|
||||
.checksum_ok:
|
||||
clc ; Clear carry on success
|
||||
|
||||
.done:
|
||||
pop di
|
||||
pop bx
|
||||
pop ax
|
||||
ret
|
||||
|
||||
; ----------------------
|
||||
; Set up a minimal GDT
|
||||
; ----------------------------------------------------------------
|
||||
; CHS Disk Read Routine
|
||||
; AL = number of sectors
|
||||
; CL = starting sector (1-based)
|
||||
; SI = destination offset (Segment:ES already set)
|
||||
; Inputs:
|
||||
; AL = sector count
|
||||
; CH = cylinder
|
||||
; DH = head
|
||||
; CL = sector (1–63, with top 2 bits as high cylinder bits)
|
||||
; SI = destination offset (segment ES must be set)
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
; Convert LBA to CHS
|
||||
; Inputs:
|
||||
; AX = LBA sector number (0-based)
|
||||
; Outputs:
|
||||
; CH = cylinder
|
||||
; DH = head
|
||||
; CL = sector (1-63, top 2 bits are upper cylinder bits)
|
||||
|
||||
lba_to_chs:
|
||||
pusha
|
||||
|
||||
xor dx, dx
|
||||
mov bx, [sectors_per_track]
|
||||
div bx ; AX = LBA / sectors_per_track, DX = remainder (sector number)
|
||||
mov si, ax ; SI = temp quotient (track index)
|
||||
mov cx, [heads_per_cylinder]
|
||||
xor dx, dx
|
||||
div cx ; AX = cylinder, DX = head
|
||||
mov ch, al ; CH = cylinder low byte
|
||||
mov dh, dl ; DH = head
|
||||
|
||||
; Now take sector number from earlier remainder
|
||||
mov cx, si ; Copy track index to CX to access CL
|
||||
and cl, 0x3F ; Mask to 6 bits (sector number)
|
||||
inc cl ; Sector numbers are 1-based
|
||||
|
||||
; Insert upper 2 bits of cylinder into CL
|
||||
mov ah, al ; AH = cylinder again
|
||||
and ah, 0xC0 ; Get top 2 bits of cylinder
|
||||
or cl, ah ; OR them into sector byte
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
read_chs:
|
||||
pusha
|
||||
push dx
|
||||
|
||||
mov cx, 5
|
||||
.retry:
|
||||
mov ah, 0x02 ; BIOS: Read sectors
|
||||
mov dl, [bootdev] ; Boot device
|
||||
; Assume CH, DH, CL already set before this call
|
||||
int 0x13
|
||||
jc .error
|
||||
pop dx
|
||||
popa
|
||||
ret
|
||||
|
||||
.error:
|
||||
dec cx
|
||||
jz disk_error
|
||||
xor ah, ah
|
||||
int 0x13
|
||||
jmp .retry
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
enable_a20:
|
||||
; Try fast A20 gate method
|
||||
in al, 0x92
|
||||
or al, 0x02
|
||||
and al, 0xFE ; Clear bit 0 to avoid fast A20 bugs
|
||||
out 0x92, al
|
||||
|
||||
; Verify A20
|
||||
call check_a20
|
||||
jnc .done ; Success
|
||||
|
||||
; Fallback: use keyboard controller method
|
||||
call .fallback
|
||||
|
||||
.done:
|
||||
ret
|
||||
|
||||
.fallback:
|
||||
mov al, 0xAD ; Disable keyboard
|
||||
out 0x64, al
|
||||
call .wait_input_clear
|
||||
|
||||
mov al, 0xD0 ; Command: read output port
|
||||
out 0x64, al
|
||||
call .wait_output_full
|
||||
in al, 0x60
|
||||
or al, 0x02 ; Set A20 enable bit
|
||||
mov bl, al
|
||||
|
||||
call .wait_input_clear
|
||||
mov al, 0xD1 ; Command: write output port
|
||||
out 0x64, al
|
||||
call .wait_input_clear
|
||||
mov al, bl
|
||||
out 0x60, al
|
||||
|
||||
call .wait_input_clear
|
||||
mov al, 0xAE ; Enable keyboard
|
||||
out 0x64, al
|
||||
ret
|
||||
|
||||
.wait_input_clear:
|
||||
in al, 0x64
|
||||
test al, 0x02
|
||||
jnz .wait_input_clear
|
||||
ret
|
||||
|
||||
.wait_output_full:
|
||||
in al, 0x64
|
||||
test al, 0x01
|
||||
jz .wait_output_full
|
||||
ret
|
||||
|
||||
check_a20:
|
||||
in al, 0x64 ; Read keyboard controller status
|
||||
test al, 0x02 ; Check if input buffer is full
|
||||
jnz check_a20 ; Wait until empty
|
||||
|
||||
mov al, 0xD0 ; Command: read output port
|
||||
out 0x64, al
|
||||
|
||||
.wait_output:
|
||||
in al, 0x60 ; Read output port value
|
||||
test al, 0x02 ; Check A20 gate bit (bit 1)
|
||||
jnz .a20_enabled ; If set, A20 enabled
|
||||
|
||||
xor al, al ; Clear carry to indicate failure
|
||||
stc ; Set carry for failure, jc will jump
|
||||
|
||||
ret
|
||||
|
||||
.a20_enabled:
|
||||
clc ; Clear carry flag to indicate success
|
||||
ret
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
gdt_start:
|
||||
dq 0x0000000000000000 ; null descriptor
|
||||
dq 0x00CF9A000000FFFF ; code segment descriptor
|
||||
dq 0x00CF92000000FFFF ; data segment descriptor
|
||||
dq 0x0000000000000000 ; Null descriptor
|
||||
dq 0x00CF9A000000FFFF ; 32-bit code segment (selector 0x08)
|
||||
dq 0x00CF92000000FFFF ; 32-bit data segment (selector 0x10)
|
||||
dq 0x00009A000000FFFF ; 16-bit code segment for real mode (selector 0x18)
|
||||
|
||||
gdt_descriptor:
|
||||
dw gdt_end - gdt_start - 1 ; size of GDT
|
||||
dd gdt_start ; address of GDT
|
||||
|
||||
dw gdt_end - gdt_start - 1
|
||||
dd gdt_start
|
||||
gdt_end:
|
||||
|
||||
setup_gdt:
|
||||
lgdt [gdt_descriptor]
|
||||
ret
|
||||
|
||||
; ----------------------
|
||||
; Switch to Protected Mode
|
||||
; ----------------------------------------------------------------
|
||||
switch_to_pm:
|
||||
cli
|
||||
mov eax, cr0
|
||||
or eax, 1
|
||||
mov cr0, eax
|
||||
jmp 0x08:protected_mode_entry
|
||||
; ----------------------
|
||||
; 32-bit Protected Mode Entry Point
|
||||
[BITS 32]
|
||||
protected_mode_entry:
|
||||
mov ax, 0x10 ; data segment selector
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
mov esp, 0x90000
|
||||
jmp 0x08:0x7E00
|
||||
|
||||
; Kernel is assumed to be loaded at 0x100000
|
||||
jmp 0x08:0x100000
|
||||
; ----------------------------------------------------------------
|
||||
print_string_16:
|
||||
.loop:
|
||||
lodsb
|
||||
or al, al
|
||||
jz .done
|
||||
mov ah, 0x0E
|
||||
int 0x10
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
disk_error_msg db "Disk error!", 0
|
||||
a20_error_msg db "A20 error!", 0
|
||||
|
||||
halt:
|
||||
cli
|
||||
hlt
|
||||
|
||||
bootdev db 0
|
||||
sectors_per_track dw 63
|
||||
heads_per_cylinder dw 255
|
||||
|
||||
; ----------------------
|
||||
; Boot signature
|
||||
times 510 - ($ - $$) db 0
|
||||
dw 0xAA55
|
||||
|
203
bootloader/boot1.asm
Normal file
203
bootloader/boot1.asm
Normal file
@@ -0,0 +1,203 @@
|
||||
; ==============================================================================
|
||||
; boot1.asm - Second Stage Bootloader (Fixed Real Mode Transition)
|
||||
; ==============================================================================
|
||||
|
||||
[BITS 32]
|
||||
global _start
|
||||
extern kmain
|
||||
|
||||
_start:
|
||||
; Set up segments
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Stack (must be identity-mapped)
|
||||
mov esp, 0x90000
|
||||
|
||||
; CPU Feature Detection: check CPUID support
|
||||
pushfd ; Save flags
|
||||
pop eax
|
||||
mov ecx, eax
|
||||
xor eax, 1 << 21 ; Flip ID bit
|
||||
push eax
|
||||
popfd
|
||||
pushfd
|
||||
pop eax
|
||||
xor eax, ecx
|
||||
jz .no_cpuid ; CPUID unsupported if no change
|
||||
|
||||
; CPUID supported, verify features
|
||||
mov eax, 1
|
||||
cpuid
|
||||
; Check for paging support (bit 31 of edx)
|
||||
test edx, 1 << 31
|
||||
jz .no_paging_support
|
||||
|
||||
; Additional CPU feature checks could be added here
|
||||
|
||||
jmp .cpuid_check_done
|
||||
|
||||
.no_cpuid:
|
||||
mov si, no_cpuid_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
.no_paging_support:
|
||||
mov si, no_paging_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
.cpuid_check_done:
|
||||
|
||||
; Temporarily switch back to real mode
|
||||
cli
|
||||
mov eax, cr0
|
||||
and eax, 0x7FFFFFFE ; Clear PE & PG bits
|
||||
mov cr0, eax
|
||||
jmp 0x18:real_mode_entry
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
[BITS 16]
|
||||
real_mode_entry:
|
||||
; Real mode for BIOS access (E820, VESA)
|
||||
xor ax, ax
|
||||
mov es, ax
|
||||
|
||||
; VESA call
|
||||
mov di, VbeControllerInfo
|
||||
mov ax, 0x4F00
|
||||
int 0x10
|
||||
jc vesa_error
|
||||
|
||||
; E820 memory map
|
||||
xor ebx, ebx
|
||||
mov edx, 0x534D4150
|
||||
mov di, MemoryMapBuffer
|
||||
mov [MemoryMapEntries], dword 0
|
||||
|
||||
.e820_loop:
|
||||
mov eax, 0xE820
|
||||
mov ecx, 24
|
||||
int 0x15
|
||||
jc e820_error
|
||||
add di, 24
|
||||
inc dword [MemoryMapEntries]
|
||||
test ebx, ebx
|
||||
jnz .e820_loop
|
||||
jmp e820_done
|
||||
|
||||
e820_error:
|
||||
mov si, e820_error_msg
|
||||
call print_string_16
|
||||
jmp halt
|
||||
|
||||
vesa_error:
|
||||
mov si, vesa_error_msg
|
||||
call print_string_16
|
||||
|
||||
; Fallback: set VGA text mode 3 and continue
|
||||
mov ah, 0x00 ; BIOS Set Video Mode function
|
||||
mov al, 0x03 ; VGA 80x25 text mode
|
||||
int 0x10
|
||||
|
||||
; Clear screen
|
||||
mov ah, 0x06 ; Scroll up function
|
||||
mov al, 0 ; Clear entire screen
|
||||
mov bh, 0x07 ; Text attribute (gray on black)
|
||||
mov cx, 0 ; Upper-left corner
|
||||
mov dx, 0x184F ; Lower-right corner
|
||||
int 0x10
|
||||
|
||||
jmp e820_done ; Continue booting without VESA graphics
|
||||
|
||||
e820_done:
|
||||
; Back to protected mode
|
||||
cli
|
||||
mov eax, cr0
|
||||
or eax, 1
|
||||
mov cr0, eax
|
||||
jmp 0x08:protected_entry
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
[BITS 16]
|
||||
print_string_16:
|
||||
.loop:
|
||||
lodsb
|
||||
or al, al
|
||||
jz .done
|
||||
mov ah, 0x0E
|
||||
int 0x10
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
e820_error_msg db "E820 Failed!", 0
|
||||
vesa_error_msg db "VESA Failed!", 0
|
||||
no_cpuid_msg db "No CPUID support detected!", 0
|
||||
no_paging_msg db "CPU lacks paging support!", 0
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
[BITS 32]
|
||||
protected_entry:
|
||||
; Paging setup
|
||||
xor eax, eax
|
||||
mov edi, page_directory
|
||||
mov ecx, 1024
|
||||
rep stosd
|
||||
mov edi, page_table
|
||||
rep stosd
|
||||
mov eax, page_table
|
||||
or eax, 0x3
|
||||
mov [page_directory], eax
|
||||
mov ecx, 1024
|
||||
mov edi, page_table
|
||||
mov eax, 0x00000003
|
||||
.fill_pages:
|
||||
mov [edi], eax
|
||||
add eax, 0x1000
|
||||
add edi, 4
|
||||
loop .fill_pages
|
||||
|
||||
mov eax, page_directory
|
||||
mov cr3, eax
|
||||
mov eax, cr0
|
||||
or eax, 0x80000000
|
||||
mov cr0, eax
|
||||
|
||||
jmp kmain
|
||||
|
||||
halt:
|
||||
cli
|
||||
|
||||
.hang:
|
||||
hlt
|
||||
jmp .hang
|
||||
|
||||
; ----------------------------------------------------------------
|
||||
; Data buffers and variables must be appropriately defined in your data section
|
||||
MemoryMapBuffer times 128 db 0 ; 128*24 bytes reserved for E820 memory map (adjust size as needed)
|
||||
MemoryMapEntries dd 0
|
||||
VbeControllerInfo times 512 db 0 ; Buffer for VESA controller info (adjust size as needed)
|
||||
|
||||
; Define page directory and page table aligned as needed (in your data section)
|
||||
align 4096
|
||||
page_directory times 1024 dd 0
|
||||
align 4096
|
||||
page_table times 1024 dd 0
|
||||
|
||||
%assign pad_size 4096
|
||||
%ifdef __SIZE__
|
||||
%define size_current __SIZE__
|
||||
%else
|
||||
%define size_current ($ - $$)
|
||||
%endif
|
||||
|
||||
%if size_current < pad_size
|
||||
times pad_size - size_current db 0
|
||||
%endif
|
||||
|
||||
checksum_byte db 0
|
@@ -1,10 +1,9 @@
|
||||
ENTRY(start)
|
||||
ENTRY(kmain)
|
||||
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
.text : {
|
||||
*(.multiboot)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
@@ -14,4 +13,14 @@ SECTIONS {
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
}
|
||||
|
||||
.stack (NOLOAD) : {
|
||||
. = ALIGN(4);
|
||||
. = . + 0x1000;
|
||||
}
|
||||
|
||||
.heap (NOLOAD) : {
|
||||
. = ALIGN(4);
|
||||
. = . + 0x10000;
|
||||
}
|
||||
}
|
||||
|
54
kernel/acpi.c
Normal file
54
kernel/acpi.c
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "acpi.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// Function to find the RSDP (Root System Description Pointer)
|
||||
acpi_rsdp_t* acpi_find_rsdp() {
|
||||
// Search memory from 0x000E0000 to 0x00100000 (BIOS)
|
||||
for (uint32_t addr = 0x000E0000; addr < 0x00100000; addr += 16) {
|
||||
acpi_rsdp_t* rsdp = (acpi_rsdp_t*)addr;
|
||||
if (memcmp(rsdp->signature, "RSD PTR ", 8) == 0) {
|
||||
uint8_t checksum = 0;
|
||||
for (size_t i = 0; i < sizeof(acpi_rsdp_t); i++) { // Change int to size_t
|
||||
checksum += ((uint8_t*)rsdp)[i];
|
||||
}
|
||||
if (checksum == 0) {
|
||||
return rsdp; // Valid RSDP found
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL; // RSDP not found
|
||||
}
|
||||
|
||||
// Function to get the RSDT or XSDT based on the RSDP revision
|
||||
void* acpi_get_rsdt_or_xsdt(acpi_rsdp_t* rsdp) {
|
||||
if (rsdp->revision >= 2) {
|
||||
return (void*)rsdp->xsdt_addr; // ACPI 2.0+ uses XSDT
|
||||
} else {
|
||||
return (void*)rsdp->rsdt_addr; // ACPI 1.0 uses RSDT
|
||||
}
|
||||
}
|
||||
|
||||
// Function to find the FADT table within the RSDT or XSDT
|
||||
acpi_fadt_t* acpi_find_fadt(void* rsdt_or_xsdt) {
|
||||
acpi_rsdt_t* rsdt = (acpi_rsdt_t*)rsdt_or_xsdt;
|
||||
uint32_t num_tables = (rsdt->length - sizeof(acpi_rsdt_t)) / sizeof(uint32_t);
|
||||
|
||||
for (size_t i = 0; i < num_tables; i++) {
|
||||
uint32_t table_addr = rsdt->tables[i];
|
||||
acpi_fadt_t* fadt = (acpi_fadt_t*)table_addr;
|
||||
if (fadt->signature == 0x50434146) {
|
||||
uint8_t checksum = 0;
|
||||
for (size_t j = 0; j < fadt->length; j++) {
|
||||
checksum += ((uint8_t*)fadt)[j];
|
||||
}
|
||||
if (checksum == 0) {
|
||||
return fadt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
47
kernel/acpi.h
Normal file
47
kernel/acpi.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef ACPI_H
|
||||
#define ACPI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ACPI base address (replace with actual value)
|
||||
#define ACPI_BASE 0xE0000000
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*)0)
|
||||
#endif
|
||||
|
||||
// ACPI RSDP Structure (Root System Description Pointer)
|
||||
typedef struct {
|
||||
uint8_t signature[8]; // Should be "RSD PTR "
|
||||
uint8_t checksum; // Checksum for the RSDP structure
|
||||
uint8_t oem_id[6]; // OEM ID string
|
||||
uint8_t revision; // ACPI revision
|
||||
uint32_t rsdt_addr; // 32-bit RSDT address (ACPI 1.0)
|
||||
uint32_t xsdt_addr; // 64-bit XSDT address (ACPI 2.0+)
|
||||
} __attribute__((packed)) acpi_rsdp_t;
|
||||
|
||||
// ACPI RSDT Structure (Root System Description Table)
|
||||
typedef struct {
|
||||
uint32_t signature; // Should be "RSDT"
|
||||
uint32_t length; // Length of the table
|
||||
uint8_t revision; // Revision of the RSDT table
|
||||
uint8_t checksum; // Checksum for the RSDT table
|
||||
uint32_t tables[]; // Array of pointers to other tables (RSDT/XSDT entries)
|
||||
} __attribute__((packed)) acpi_rsdt_t;
|
||||
|
||||
// ACPI FADT Structure (Fixed ACPI Description Table)
|
||||
typedef struct {
|
||||
uint32_t signature; // Should be "FACP"
|
||||
uint32_t length; // Length of the table
|
||||
uint8_t revision; // Revision of the FADT table
|
||||
uint8_t checksum; // Checksum for the FADT table
|
||||
uint32_t pm_tmr_address; // Power Management Timer Address
|
||||
// ... other FADT fields
|
||||
} __attribute__((packed)) acpi_fadt_t;
|
||||
|
||||
// Function prototypes
|
||||
acpi_rsdp_t* acpi_find_rsdp();
|
||||
void* acpi_get_rsdt_or_xsdt(acpi_rsdp_t* rsdp);
|
||||
acpi_fadt_t* acpi_find_fadt(void* rsdt_or_xsdt);
|
||||
|
||||
#endif /* ACPI_H */
|
37
kernel/cpu.c
Normal file
37
kernel/cpu.c
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "cpu.h"
|
||||
#include "serial.h"
|
||||
#include "terminal.h"
|
||||
#include "utils.h"
|
||||
#include "print.h"
|
||||
|
||||
void cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
|
||||
asm volatile (
|
||||
"cpuid"
|
||||
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
|
||||
: "a"(function)
|
||||
);
|
||||
}
|
||||
|
||||
void identify_cpu() {
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
char vendor[13];
|
||||
|
||||
cpuid(0, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
*(uint32_t *)&vendor[0] = ebx;
|
||||
*(uint32_t *)&vendor[4] = edx;
|
||||
*(uint32_t *)&vendor[8] = ecx;
|
||||
vendor[12] = '\0';
|
||||
|
||||
terminal_write("CPU Vendor: ");
|
||||
terminal_write(vendor);
|
||||
terminal_write("\n");
|
||||
|
||||
serial_write("CPU Vendor: ");
|
||||
serial_write(vendor);
|
||||
serial_write("\n");
|
||||
|
||||
terminal_write("CPUID max leaf: ");
|
||||
print_hex(eax); // You must implement this (see below)
|
||||
terminal_write("\n");
|
||||
}
|
9
kernel/cpu.h
Normal file
9
kernel/cpu.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef CPU_H
|
||||
#define CPU_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
|
||||
void identify_cpu(void);
|
||||
|
||||
#endif // CPU_H
|
45
kernel/debug.c
Normal file
45
kernel/debug.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "debug.h"
|
||||
#include "vga.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define VGA_WIDTH 80
|
||||
#define VGA_HEIGHT 25
|
||||
#define VGA_MEMORY 0xB8000
|
||||
|
||||
// VGA text mode color attributes
|
||||
#define COLOR_WHITE 0x07
|
||||
|
||||
// Pointer to the VGA memory
|
||||
volatile uint16_t* vga_buffer = (uint16_t*)VGA_MEMORY;
|
||||
|
||||
// Function to print a string to the VGA text buffer
|
||||
void debug_print(const char *str) {
|
||||
while (*str) {
|
||||
if (*str == '\n') {
|
||||
// Handle new line
|
||||
// Move to the next line (not implemented here)
|
||||
// You can implement line wrapping if needed
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate the position in the VGA buffer
|
||||
static int cursor_x = 0;
|
||||
static int cursor_y = 0;
|
||||
|
||||
// Write the character and its attribute to the VGA buffer
|
||||
vga_buffer[cursor_y * VGA_WIDTH + cursor_x] = (COLOR_WHITE << 8) | *str;
|
||||
|
||||
// Move the cursor to the right
|
||||
cursor_x++;
|
||||
if (cursor_x >= VGA_WIDTH) {
|
||||
cursor_x = 0;
|
||||
cursor_y++;
|
||||
if (cursor_y >= VGA_HEIGHT) {
|
||||
cursor_y = 0; // Scroll up (not implemented here)
|
||||
}
|
||||
}
|
||||
|
||||
str++;
|
||||
}
|
||||
}
|
6
kernel/debug.h
Normal file
6
kernel/debug.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
void debug_print(const char *str);
|
||||
|
||||
#endif // DEBUG_H
|
36
kernel/display.c
Normal file
36
kernel/display.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "display.h"
|
||||
#include "io.h" // Include your I/O header for port access
|
||||
#include "vga.h"
|
||||
|
||||
// Initialize the display
|
||||
void init_display(void) {
|
||||
// Initialize VGA settings, if necessary
|
||||
// This could involve setting up the VGA mode, etc.
|
||||
set_display_mode(0x13); // Example: Set to 320x200 256-color mode
|
||||
}
|
||||
|
||||
// Enumerate connected displays
|
||||
void enumerate_displays(void) {
|
||||
// This is a simplified example. Actual enumeration may require
|
||||
// reading from specific VGA registers or using BIOS interrupts.
|
||||
|
||||
// For demonstration, we will just print a message
|
||||
// In a real driver, you would check the VGA registers
|
||||
// to determine connected displays.
|
||||
clear_display();
|
||||
// Here you would typically read from VGA registers to find connected displays
|
||||
// For example, using inb() to read from VGA ports
|
||||
}
|
||||
|
||||
// Set the display mode
|
||||
void set_display_mode(uint8_t mode) {
|
||||
// Set the VGA mode by writing to the appropriate registers
|
||||
outb(VGA_PORT, mode); // Example function to write to a port
|
||||
}
|
||||
|
||||
// Clear the display
|
||||
void clear_display(void) {
|
||||
// Clear the display by filling it with a color
|
||||
// This is a placeholder for actual clearing logic
|
||||
// You would typically write to video memory here
|
||||
}
|
14
kernel/display.h
Normal file
14
kernel/display.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef DISPLAY_H
|
||||
#define DISPLAY_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define VGA_PORT 0x3C0 // Base port for VGA
|
||||
|
||||
// Function prototypes
|
||||
void init_display(void);
|
||||
void enumerate_displays(void);
|
||||
void set_display_mode(uint8_t mode);
|
||||
void clear_display(void);
|
||||
|
||||
#endif // DISPLAY_H
|
50
kernel/elf.c
Normal file
50
kernel/elf.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "elf.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// This assumes that the elf is ELF32 and little-endian Intel X86 architecture.
|
||||
|
||||
// Check if a binary is a valid ELF executable.
|
||||
int elf_validate(const void* data) {
|
||||
const Elf32_Ehdr* header = (const Elf32_Ehdr*)data;
|
||||
|
||||
if (*(uint32_t*)header->e_ident != ELF_MAGIC)
|
||||
return 0;
|
||||
|
||||
if (header->e_type != ET_EXEC && header->e_type != ET_DYN)
|
||||
return 0;
|
||||
|
||||
if (header->e_machine != EM_386)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load an ELF executable into memory.
|
||||
int elf_load(const void* data, void (*load_segment)(uint32_t vaddr, const void* src, uint32_t size)) {
|
||||
const Elf32_Ehdr* header = (const Elf32_Ehdr*)data;
|
||||
const Elf32_Phdr* ph = (const Elf32_Phdr*)((uint8_t*)data + header->e_phoff);
|
||||
|
||||
for (int i = 0; i < header->e_phnum; i++) {
|
||||
if (ph[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
const void* src = (uint8_t*)data + ph[i].p_offset;
|
||||
uint32_t vaddr = ph[i].p_vaddr;
|
||||
uint32_t filesz = ph[i].p_filesz;
|
||||
uint32_t memsz = ph[i].p_memsz;
|
||||
|
||||
// Copy data segment
|
||||
load_segment(vaddr, src, filesz);
|
||||
|
||||
// Zero remaining BSS (if any)
|
||||
if (memsz > filesz) {
|
||||
uint8_t* bss_start = (uint8_t*)(vaddr + filesz);
|
||||
for (uint32_t j = 0; j < memsz - filesz; j++) {
|
||||
bss_start[j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return header->e_entry;
|
||||
}
|
52
kernel/elf.h
Normal file
52
kernel/elf.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef ELF_H
|
||||
#define ELF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ELF_MAGIC 0x464C457F // "\x7FELF" in little-endian
|
||||
|
||||
// ELF Types
|
||||
#define ET_EXEC 2
|
||||
#define ET_DYN 3
|
||||
|
||||
// ELF Machine
|
||||
#define EM_386 3
|
||||
|
||||
// ELF Ident indexes
|
||||
#define EI_NIDENT 16
|
||||
|
||||
// Program header types
|
||||
#define PT_NULL 0
|
||||
#define PT_LOAD 1
|
||||
|
||||
// ELF Header (32-bit)
|
||||
typedef struct {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry; // Entry point
|
||||
uint32_t e_phoff; // Program header table offset
|
||||
uint32_t e_shoff; // Section header table offset
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} __attribute__((packed)) Elf32_Ehdr;
|
||||
|
||||
// Program Header (32-bit)
|
||||
typedef struct {
|
||||
uint32_t p_type;
|
||||
uint32_t p_offset;
|
||||
uint32_t p_vaddr;
|
||||
uint32_t p_paddr;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
} __attribute__((packed)) Elf32_Phdr;
|
||||
|
||||
#endif // ELF_H
|
5
kernel/fat12.c
Normal file
5
kernel/fat12.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "fat12.h"
|
||||
|
||||
void fat12_init() {
|
||||
// Filesystem initialization code
|
||||
}
|
47
kernel/fat12.h
Normal file
47
kernel/fat12.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef FAT12_H
|
||||
#define FAT12_H
|
||||
|
||||
#include <stdint.h> /* Include standard integer types */
|
||||
#include <stdio.h> /* Include standard I/O library */
|
||||
#include <stdlib.h> /* Include standard library */
|
||||
|
||||
#define FAT12_SECTOR_SIZE 512 /* Sector size for FAT12 */
|
||||
#define FAT12_MAX_FILES 128 /* Maximum number of files in root directory */
|
||||
#define FAT12_ROOT_DIR_SECTORS 1 /* Number of sectors for root directory */
|
||||
|
||||
typedef struct {
|
||||
uint8_t jump[3]; /* Jump instruction for boot */
|
||||
char oem[8]; /* OEM name */
|
||||
uint16_t bytes_per_sector; /* Bytes per sector */
|
||||
uint8_t sectors_per_cluster; /* Sectors per cluster */
|
||||
uint16_t reserved_sectors; /* Reserved sectors count */
|
||||
uint8_t num_fats; /* Number of FATs */
|
||||
uint16_t max_root_dir_entries; /* Max entries in root directory */
|
||||
uint16_t total_sectors; /* Total sectors */
|
||||
uint8_t media_descriptor; /* Media descriptor */
|
||||
uint16_t fat_size; /* Size of each FAT */
|
||||
uint16_t sectors_per_track; /* Sectors per track */
|
||||
uint16_t num_heads; /* Number of heads */
|
||||
uint32_t hidden_sectors; /* Hidden sectors count */
|
||||
uint32_t total_sectors_large; /* Total sectors for large disks */
|
||||
} __attribute__((packed)) FAT12_BootSector; /* Packed structure for boot sector */
|
||||
|
||||
typedef struct {
|
||||
char name[11]; /* File name (8.3 format) */
|
||||
uint8_t attr; /* File attributes */
|
||||
uint16_t reserved; /* Reserved */
|
||||
uint16_t time; /* Time of last write */
|
||||
uint16_t date; /* Date of last write */
|
||||
uint16_t start_cluster; /* Starting cluster number */
|
||||
uint32_t file_size; /* File size in bytes */
|
||||
} __attribute__((packed)) FAT12_DirEntry; /* Directory entry structure */
|
||||
|
||||
void initialize_fat12(const char *disk_image); /* Function to initialize FAT12 */
|
||||
void read_fat12(const char *disk_image); /* Function to read FAT12 */
|
||||
void write_fat12(const char *disk_image); /* Function to write FAT12 */
|
||||
void list_files(const char *disk_image); /* Function to list files in root directory */
|
||||
void read_file(const char *disk_image, const char *filename); /* Function to read a file */
|
||||
void write_file(const char *disk_image, const char *filename, const uint8_t *data, size_t size); /* Function to write a file */
|
||||
|
||||
#endif
|
||||
/* FAT12_H */
|
0
kernel/framebuffer.c
Normal file
0
kernel/framebuffer.c
Normal file
0
kernel/framebuffer.h
Normal file
0
kernel/framebuffer.h
Normal file
21
kernel/gdt.asm
Normal file
21
kernel/gdt.asm
Normal file
@@ -0,0 +1,21 @@
|
||||
; gdt.asm
|
||||
; Assembler function to load the GDT and update segment registers
|
||||
|
||||
global gdt_flush
|
||||
|
||||
gdt_flush:
|
||||
mov eax, [esp + 4] ; Argument: pointer to GDT descriptor
|
||||
lgdt [eax] ; Load GDT
|
||||
|
||||
; Reload segment registers
|
||||
mov ax, 0x10 ; 0x10 = offset to kernel data segment (3rd entry)
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Jump to flush the instruction pipeline and load CS
|
||||
jmp 0x08:.flush ; 0x08 = offset to code segment (2nd entry)
|
||||
.flush:
|
||||
ret
|
48
kernel/gdt.c
Normal file
48
kernel/gdt.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "gdt.h"
|
||||
|
||||
// Structure of a GDT entry (8 bytes)
|
||||
struct gdt_entry {
|
||||
uint16_t limit_low; // Limit bits 0–15
|
||||
uint16_t base_low; // Base bits 0–15
|
||||
uint8_t base_middle; // Base bits 16–23
|
||||
uint8_t access; // Access flags
|
||||
uint8_t granularity; // Granularity + limit bits 16–19
|
||||
uint8_t base_high; // Base bits 24–31
|
||||
} __attribute__((packed));
|
||||
|
||||
// Structure of the GDT pointer
|
||||
struct gdt_ptr {
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
} __attribute__((packed));
|
||||
|
||||
// Declare GDT with 3 entries
|
||||
static struct gdt_entry gdt[3];
|
||||
static struct gdt_ptr gp;
|
||||
|
||||
// External ASM function to load GDT
|
||||
extern void gdt_flush(uint32_t);
|
||||
|
||||
// Set an individual GDT entry
|
||||
static void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t granularity) {
|
||||
gdt[num].base_low = (base & 0xFFFF);
|
||||
gdt[num].base_middle = (base >> 16) & 0xFF;
|
||||
gdt[num].base_high = (base >> 24) & 0xFF;
|
||||
|
||||
gdt[num].limit_low = (limit & 0xFFFF);
|
||||
gdt[num].granularity = ((limit >> 16) & 0x0F) | (granularity & 0xF0);
|
||||
|
||||
gdt[num].access = access;
|
||||
}
|
||||
|
||||
// Initialize the GDT
|
||||
void gdt_init(void) {
|
||||
gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
|
||||
gp.base = (uint32_t)&gdt;
|
||||
|
||||
gdt_set_gate(0, 0, 0, 0, 0); // Null segment
|
||||
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
|
||||
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
|
||||
|
||||
gdt_flush((uint32_t)&gp);
|
||||
}
|
8
kernel/gdt.h
Normal file
8
kernel/gdt.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef GDT_H
|
||||
#define GDT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void gdt_init(void);
|
||||
|
||||
#endif
|
0
kernel/gui.c
Normal file
0
kernel/gui.c
Normal file
0
kernel/gui.h
Normal file
0
kernel/gui.h
Normal file
63
kernel/heap.c
Normal file
63
kernel/heap.c
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "heap.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct heap_block {
|
||||
size_t size;
|
||||
struct heap_block *next;
|
||||
int is_free;
|
||||
} heap_block_t;
|
||||
|
||||
static heap_block_t *free_list = NULL;
|
||||
static void *heap_start_ptr = NULL;
|
||||
static void *heap_end_ptr = NULL;
|
||||
|
||||
void heap_init(void *heap_start, void *heap_end) {
|
||||
heap_start_ptr = heap_start;
|
||||
heap_end_ptr = heap_end;
|
||||
|
||||
free_list = (heap_block_t *)heap_start;
|
||||
free_list->size = (uintptr_t)heap_end - (uintptr_t)heap_start - sizeof(heap_block_t);
|
||||
free_list->next = NULL;
|
||||
free_list->is_free = 1;
|
||||
}
|
||||
|
||||
void *heap_alloc(size_t size) {
|
||||
heap_block_t *current = free_list;
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->is_free && current->size >= size) {
|
||||
// If it's a large block, split it
|
||||
if (current->size > size + sizeof(heap_block_t)) {
|
||||
heap_block_t *new_block = (heap_block_t *)((uintptr_t)current + sizeof(heap_block_t) + size);
|
||||
new_block->size = current->size - size - sizeof(heap_block_t);
|
||||
new_block->next = current->next;
|
||||
new_block->is_free = 1;
|
||||
|
||||
current->next = new_block;
|
||||
current->size = size;
|
||||
}
|
||||
|
||||
current->is_free = 0;
|
||||
return (void *)((uintptr_t)current + sizeof(heap_block_t));
|
||||
}
|
||||
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return NULL; // Out of memory
|
||||
}
|
||||
|
||||
void heap_free(void *ptr) {
|
||||
if (ptr == NULL) return;
|
||||
|
||||
heap_block_t *block = (heap_block_t *)((uintptr_t)ptr - sizeof(heap_block_t));
|
||||
block->is_free = 1;
|
||||
|
||||
// Coalesce with next block
|
||||
if (block->next && block->next->is_free) {
|
||||
block->size += block->next->size + sizeof(heap_block_t);
|
||||
block->next = block->next->next;
|
||||
}
|
||||
|
||||
// TODO: Coalesce with previous block for better compaction
|
||||
}
|
11
kernel/heap.h
Normal file
11
kernel/heap.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef HEAP_H
|
||||
#define HEAP_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void heap_init(void *heap_start, void *heap_end);
|
||||
|
||||
void *heap_alloc(size_t size);
|
||||
void heap_free(void *ptr);
|
||||
|
||||
#endif // HEAP_H
|
66
kernel/idt.c
66
kernel/idt.c
@@ -8,7 +8,37 @@ idt_ptr_t idt_ptr;
|
||||
|
||||
// External assembly stubs for ISRs (provided below)
|
||||
extern void isr0();
|
||||
extern void isr1();
|
||||
extern void isr2();
|
||||
extern void isr3();
|
||||
extern void isr4();
|
||||
extern void isr5();
|
||||
extern void isr6();
|
||||
extern void isr7();
|
||||
extern void isr8();
|
||||
extern void isr9();
|
||||
extern void isr10();
|
||||
extern void isr11();
|
||||
extern void isr12();
|
||||
extern void isr13();
|
||||
extern void isr14();
|
||||
extern void isr15();
|
||||
extern void isr16();
|
||||
extern void isr17();
|
||||
extern void isr18();
|
||||
extern void isr19();
|
||||
extern void isr20();
|
||||
extern void isr21();
|
||||
extern void isr22();
|
||||
extern void isr23();
|
||||
extern void isr24();
|
||||
extern void isr25();
|
||||
extern void isr26();
|
||||
extern void isr27();
|
||||
extern void isr28();
|
||||
extern void isr29();
|
||||
extern void isr30();
|
||||
extern void isr31();
|
||||
extern void isr_default();
|
||||
|
||||
// Helper to set an IDT gate
|
||||
@@ -36,8 +66,40 @@ void idt_init() {
|
||||
}
|
||||
|
||||
// Set specific handlers
|
||||
idt_set_gate(0, (uint32_t)isr0); // Divide by zero
|
||||
idt_set_gate(13, (uint32_t)isr13); // General protection fault
|
||||
// Assign CPU exception handlers
|
||||
idt_set_gate(0, (uint32_t)isr0);
|
||||
idt_set_gate(1, (uint32_t)isr1);
|
||||
idt_set_gate(2, (uint32_t)isr2);
|
||||
idt_set_gate(3, (uint32_t)isr3);
|
||||
idt_set_gate(4, (uint32_t)isr4);
|
||||
idt_set_gate(5, (uint32_t)isr5);
|
||||
idt_set_gate(6, (uint32_t)isr6);
|
||||
idt_set_gate(7, (uint32_t)isr7);
|
||||
idt_set_gate(8, (uint32_t)isr8);
|
||||
idt_set_gate(9, (uint32_t)isr9);
|
||||
idt_set_gate(10, (uint32_t)isr10);
|
||||
idt_set_gate(11, (uint32_t)isr11);
|
||||
idt_set_gate(12, (uint32_t)isr12);
|
||||
idt_set_gate(13, (uint32_t)isr13);
|
||||
idt_set_gate(14, (uint32_t)isr14);
|
||||
idt_set_gate(15, (uint32_t)isr15);
|
||||
idt_set_gate(16, (uint32_t)isr16);
|
||||
idt_set_gate(17, (uint32_t)isr17);
|
||||
idt_set_gate(18, (uint32_t)isr18);
|
||||
idt_set_gate(19, (uint32_t)isr19);
|
||||
idt_set_gate(20, (uint32_t)isr20);
|
||||
idt_set_gate(21, (uint32_t)isr21);
|
||||
idt_set_gate(22, (uint32_t)isr22);
|
||||
idt_set_gate(23, (uint32_t)isr23);
|
||||
idt_set_gate(24, (uint32_t)isr24);
|
||||
idt_set_gate(25, (uint32_t)isr25);
|
||||
idt_set_gate(26, (uint32_t)isr26);
|
||||
idt_set_gate(27, (uint32_t)isr27);
|
||||
idt_set_gate(28, (uint32_t)isr28);
|
||||
idt_set_gate(29, (uint32_t)isr29);
|
||||
idt_set_gate(30, (uint32_t)isr30);
|
||||
idt_set_gate(31, (uint32_t)isr31);
|
||||
|
||||
|
||||
idt_load();
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
#ifndef IO_H
|
||||
#define IO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val) {
|
||||
asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
5
kernel/irq.c
Normal file
5
kernel/irq.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "irq.h"
|
||||
|
||||
void irq_init() {
|
||||
// IRQ initialization code
|
||||
}
|
6
kernel/irq.h
Normal file
6
kernel/irq.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef IRQ_H
|
||||
#define IRQ_H
|
||||
|
||||
void irq_init();
|
||||
|
||||
#endif // IRQ_H
|
@@ -1,29 +1,79 @@
|
||||
; isr.asm
|
||||
[BITS 32]
|
||||
[GLOBAL isr0, isr13, isr_default]
|
||||
[GLOBAL isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8, isr9]
|
||||
[GLOBAL isr10, isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19]
|
||||
[GLOBAL isr20, isr21, isr22, isr23, isr24, isr25, isr26, isr27, isr28, isr29]
|
||||
[GLOBAL isr30, isr31, isr_default]
|
||||
|
||||
isr0:
|
||||
[EXTERN isr_handler]
|
||||
|
||||
%macro ISR_NOERR 1
|
||||
isr%1:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 0
|
||||
push dword 0 ; Dummy error code
|
||||
push dword %1 ; Interrupt number
|
||||
call isr_handler
|
||||
add esp, 8
|
||||
sti
|
||||
iret
|
||||
%endmacro
|
||||
|
||||
isr13:
|
||||
%macro ISR_ERR 1
|
||||
isr%1:
|
||||
cli
|
||||
push byte 13
|
||||
push byte 0
|
||||
push dword %1 ; Interrupt number
|
||||
call isr_handler
|
||||
add esp, 8
|
||||
sti
|
||||
iret
|
||||
%endmacro
|
||||
|
||||
; ISR 0–7: No error code
|
||||
ISR_NOERR 0
|
||||
ISR_NOERR 1
|
||||
ISR_NOERR 2
|
||||
ISR_NOERR 3
|
||||
ISR_NOERR 4
|
||||
ISR_NOERR 5
|
||||
ISR_NOERR 6
|
||||
ISR_NOERR 7
|
||||
|
||||
; ISR 8–14: Error code pushed automatically
|
||||
ISR_ERR 8
|
||||
ISR_NOERR 9 ; Coprocessor Segment Overrun (obsolete, no error code)
|
||||
ISR_ERR 10
|
||||
ISR_ERR 11
|
||||
ISR_ERR 12
|
||||
ISR_ERR 13
|
||||
ISR_ERR 14
|
||||
|
||||
; ISR 15 is reserved
|
||||
ISR_NOERR 15
|
||||
|
||||
; ISR 16–19 (FPU, Alignment Check, etc.)
|
||||
ISR_NOERR 16
|
||||
ISR_ERR 17
|
||||
ISR_NOERR 18
|
||||
ISR_NOERR 19
|
||||
|
||||
; ISR 20–31 (reserved or future use)
|
||||
ISR_NOERR 20
|
||||
ISR_NOERR 21
|
||||
ISR_NOERR 22
|
||||
ISR_NOERR 23
|
||||
ISR_NOERR 24
|
||||
ISR_NOERR 25
|
||||
ISR_NOERR 26
|
||||
ISR_NOERR 27
|
||||
ISR_NOERR 28
|
||||
ISR_NOERR 29
|
||||
ISR_NOERR 30
|
||||
ISR_NOERR 31
|
||||
|
||||
; Fallback handler
|
||||
isr_default:
|
||||
cli
|
||||
push byte 255
|
||||
push byte 0
|
||||
push dword 255
|
||||
push dword 0
|
||||
call isr_handler
|
||||
add esp, 8
|
||||
sti
|
||||
|
50
kernel/isr.c
50
kernel/isr.c
@@ -1,21 +1,53 @@
|
||||
#include "terminal.h"
|
||||
#include "serial.h"
|
||||
#include "isr.h"
|
||||
#include "io.h"
|
||||
#include "utils.h"
|
||||
|
||||
static isr_callback_t interrupt_handlers[MAX_INTERRUPTS] = { 0 };
|
||||
|
||||
void isr_handler(uint32_t int_num, uint32_t err_code) {
|
||||
terminal_write("Interrupt occurred: ");
|
||||
// Add simple int-to-string printing here
|
||||
|
||||
print_hex(int_num);
|
||||
terminal_write("\n");
|
||||
|
||||
serial_write("INT triggered\n");
|
||||
|
||||
if (int_num == 0) {
|
||||
terminal_write(" -> Divide by zero error!\n");
|
||||
} else if (int_num == 13) {
|
||||
terminal_write(" -> General Protection Fault!\n");
|
||||
terminal_write("Error code: ");
|
||||
print_hex(err_code);
|
||||
terminal_write("\n");
|
||||
|
||||
if (interrupt_handlers[int_num]) {
|
||||
interrupt_handlers[int_num](); // Call registered handler
|
||||
} else {
|
||||
terminal_write(" -> Unknown interrupt\n");
|
||||
terminal_write(" -> No handler registered\n");
|
||||
|
||||
if (int_num == 0) {
|
||||
terminal_write(" -> Divide by zero error!\n");
|
||||
} else if (int_num == 13) {
|
||||
terminal_write(" -> General Protection Fault!\n");
|
||||
} else {
|
||||
terminal_write(" -> Unknown interrupt\n");
|
||||
}
|
||||
|
||||
// Halt CPU
|
||||
while (1) {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
// Halt CPU
|
||||
while (1) {
|
||||
asm volatile ("hlt");
|
||||
// === Send End Of Interrupt to PIC(s) ===
|
||||
if (int_num >= 40) {
|
||||
// Send reset signal to slave PIC
|
||||
outb(0xA0, 0x20);
|
||||
}
|
||||
if (int_num >= 32) {
|
||||
// Send reset signal to master PIC
|
||||
outb(0x20, 0x20);
|
||||
}
|
||||
}
|
||||
|
||||
void register_interrupt_handler(uint8_t n, isr_callback_t handler) {
|
||||
interrupt_handlers[n] = handler;
|
||||
}
|
||||
|
13
kernel/isr.h
Normal file
13
kernel/isr.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef ISR_H
|
||||
#define ISR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_INTERRUPTS 256
|
||||
|
||||
typedef void (*isr_callback_t)(void);
|
||||
|
||||
void isr_handler(uint32_t int_num, uint32_t err_code);
|
||||
void register_interrupt_handler(uint8_t n, isr_callback_t handler);
|
||||
|
||||
#endif
|
59
kernel/keyboard.c
Normal file
59
kernel/keyboard.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "keyboard.h"
|
||||
#include "io.h"
|
||||
#include "isr.h"
|
||||
#include "terminal.h"
|
||||
|
||||
#define KEYBOARD_DATA_PORT 0x60
|
||||
|
||||
static char key_buffer[256];
|
||||
static uint8_t buffer_index = 0;
|
||||
|
||||
// Basic US QWERTY keymap (scancode to ASCII)
|
||||
static const char scancode_map[128] = {
|
||||
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', // 0x00 - 0x09
|
||||
'9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', // 0x0A - 0x13
|
||||
't', 'y', 'z', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x14 - 0x1D
|
||||
0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', // 0x1E - 0x27
|
||||
';', '\'', '`', 0, '\\', 'x', 'c', 'v', 'b', // 0x28 - 0x31
|
||||
'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, // 0x32 - 0x3B
|
||||
// rest can be filled as needed
|
||||
};
|
||||
|
||||
// Interrupt handler for IRQ1
|
||||
void keyboard_callback(void) {
|
||||
uint8_t scancode = inb(0x60);
|
||||
|
||||
// Only handle key press (ignore key release)
|
||||
if (!(scancode & 0x80)) {
|
||||
char c = scancode_map[scancode];
|
||||
if (c && buffer_index < sizeof(key_buffer) - 1) {
|
||||
key_buffer[buffer_index++] = c;
|
||||
terminal_putchar(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Send End of Interrupt (EOI) to the PIC
|
||||
outb(0x20, 0x20);
|
||||
}
|
||||
|
||||
|
||||
void keyboard_init() {
|
||||
register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21)
|
||||
}
|
||||
|
||||
// Blocking read (returns one char)
|
||||
char keyboard_get_char() {
|
||||
while (buffer_index == 0); // Busy wait
|
||||
|
||||
char c;
|
||||
__asm__ __volatile__("cli");
|
||||
c = key_buffer[0];
|
||||
for (uint8_t i = 1; i < buffer_index; i++) {
|
||||
key_buffer[i - 1] = key_buffer[i];
|
||||
}
|
||||
buffer_index--;
|
||||
__asm__ __volatile__("sti");
|
||||
|
||||
return c;
|
||||
}
|
||||
|
7
kernel/keyboard.h
Normal file
7
kernel/keyboard.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef KEYBOARD_H
|
||||
#define KEYBOARD_H
|
||||
|
||||
void keyboard_init(void);
|
||||
char keyboard_get_char(void); // Blocking read from buffer
|
||||
|
||||
#endif
|
@@ -6,6 +6,12 @@
|
||||
#include "idt.h"
|
||||
#include "paging.h"
|
||||
#include "memmap.h"
|
||||
#include "gdt.h"
|
||||
#include "cpu.h"
|
||||
#include "kmalloc.h"
|
||||
#include "timer.h"
|
||||
#include "utils.h"
|
||||
#include "keyboard.h"
|
||||
|
||||
#define LPT1 0x378
|
||||
|
||||
@@ -21,8 +27,16 @@ void kmain(void) {
|
||||
serial_init();
|
||||
serial_write("Serial port initialized.\n");
|
||||
|
||||
terminal_write("Identifying CPU...\n");
|
||||
identify_cpu();
|
||||
serial_write("CPU identification complete.\n");
|
||||
|
||||
lpt_write('L'); // Send 'L' to LPT1 to test
|
||||
|
||||
terminal_write("Initializing GDT...\n");
|
||||
gdt_init();
|
||||
serial_write("GDT initialized.\n");
|
||||
|
||||
terminal_write("Initializing IDT...\n");
|
||||
idt_init();
|
||||
serial_write("IDT initialized.\n");
|
||||
@@ -31,17 +45,37 @@ void kmain(void) {
|
||||
paging_init();
|
||||
serial_write("Paging initialized.\n");
|
||||
|
||||
terminal_write("Initializing memory allocator...\n");
|
||||
kmalloc_init(0xC0100000); // Virtual heap start address (must be mapped!)
|
||||
serial_write("kmalloc initialized.\n");
|
||||
|
||||
serial_write("Allocated 128 bytes.\n");
|
||||
|
||||
terminal_write("Initializing timer...\n");
|
||||
timer_init(100); // 100 Hz (10 ms interval)
|
||||
serial_write("Timer initialized.\n");
|
||||
|
||||
terminal_write("Initializing keyboard...\n");
|
||||
keyboard_init();
|
||||
serial_write("Keyboard initialized.\n");
|
||||
|
||||
terminal_write("Getting memory map...\n");
|
||||
memory_map_entry_t mmap[32];
|
||||
uint32_t mmap_size = get_memory_map(mmap, 32);
|
||||
serial_write("Memory map retrieved.\n");
|
||||
|
||||
terminal_write("Memory Regions:\n");
|
||||
|
||||
char buf[32];
|
||||
for (uint32_t i = 0; i < mmap_size; i++) {
|
||||
terminal_write(" - Region: ");
|
||||
// You would format and print base/length/type here
|
||||
// (e.g., with a basic itoa and print_hex helper)
|
||||
serial_write("Memory region entry\n");
|
||||
terminal_write(" - Base: ");
|
||||
print_hex((uint32_t)(mmap[i].base_addr & 0xFFFFFFFF)); // Lower 32 bits
|
||||
terminal_write(", Length: ");
|
||||
print_hex((uint32_t)(mmap[i].length & 0xFFFFFFFF)); // Lower 32 bits
|
||||
terminal_write(", Type: ");
|
||||
itoa(mmap[i].type, buf, 10);
|
||||
terminal_write(buf);
|
||||
terminal_write("\n");
|
||||
}
|
||||
|
||||
terminal_write("System initialized. Halting.\n");
|
||||
|
51
kernel/kmalloc.c
Normal file
51
kernel/kmalloc.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "kmalloc.h"
|
||||
#include "terminal.h" // Optional: for debug output
|
||||
|
||||
#define HEAP_END 0xC0100000
|
||||
|
||||
static uint32_t current_heap = 0;
|
||||
|
||||
// Initialize the allocator with a starting heap address
|
||||
void kmalloc_init(uint32_t heap_start) {
|
||||
current_heap = heap_start;
|
||||
}
|
||||
|
||||
// Simple bump allocator
|
||||
void* kmalloc(size_t size) {
|
||||
if (current_heap == 0) {
|
||||
terminal_write("kmalloc used before initialization!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* addr = (void*)current_heap;
|
||||
current_heap += size;
|
||||
return addr;
|
||||
}
|
||||
|
||||
// Allocate memory aligned to a power-of-two boundary (e.g., 0x1000)
|
||||
void* kmalloc_aligned(size_t size, uint32_t alignment) {
|
||||
if (current_heap == 0) {
|
||||
terminal_write("kmalloc_aligned used before initialization!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Align the current_heap pointer
|
||||
if ((current_heap & (alignment - 1)) != 0) {
|
||||
current_heap = (current_heap + alignment) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
if (current_heap + size > HEAP_END) {
|
||||
terminal_write("kmalloc_aligned: Out of memory!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* addr = (void*)current_heap;
|
||||
current_heap += size;
|
||||
return addr;
|
||||
}
|
||||
|
||||
void kfree(void* ptr) {
|
||||
// In a bump allocator, we cannot free individual blocks.
|
||||
// We can reset the allocator to the initial state.
|
||||
current_heap = 0; // Reset the heap pointer
|
||||
}
|
11
kernel/kmalloc.h
Normal file
11
kernel/kmalloc.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef KMALLOC_H
|
||||
#define KMALLOC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
void kmalloc_init(uint32_t heap_start);
|
||||
void* kmalloc(size_t size);
|
||||
void* kmalloc_aligned(size_t size, uint32_t alignment);
|
||||
|
||||
#endif
|
31
kernel/linker.ld
Normal file
31
kernel/linker.ld
Normal file
@@ -0,0 +1,31 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
.multiboot : {
|
||||
*(.multiboot)
|
||||
}
|
||||
|
||||
.text : {
|
||||
*(.text)
|
||||
}
|
||||
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
__stack_top = .;
|
||||
. += 128K;
|
||||
__stack_bottom = .;
|
||||
}
|
105
kernel/malloc.c
Normal file
105
kernel/malloc.c
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "malloc.h"
|
||||
#include <stdint.h>
|
||||
|
||||
static void *heap_start; // Start of the heap
|
||||
static void *heap_end; // End of the heap
|
||||
static struct memory_block *free_blocks; // List of free blocks
|
||||
|
||||
void init_heap(void *start, void *end)
|
||||
{
|
||||
heap_start = start;
|
||||
heap_end = end;
|
||||
|
||||
// Initialize the heap with a single large free block
|
||||
free_blocks = (struct memory_block *)start;
|
||||
free_blocks->size = (uintptr_t)end - (uintptr_t)start - sizeof(struct memory_block);
|
||||
free_blocks->next = NULL;
|
||||
free_blocks->is_free = 1;
|
||||
}
|
||||
|
||||
void *find_free_block(size_t size) {
|
||||
struct memory_block *current = free_blocks;
|
||||
while (current != NULL) {
|
||||
if (current->is_free && current->size >= size) {
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
// No suitable block found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mark_as_used(void *ptr, size_t size) {
|
||||
struct memory_block *block = (struct memory_block *)ptr;
|
||||
block->is_free = 0;
|
||||
|
||||
// If the block is larger than needed, split it
|
||||
if (block->size > size + sizeof(struct memory_block)) {
|
||||
struct memory_block *new_block = (struct memory_block *)((uintptr_t)ptr + size + sizeof(struct memory_block));
|
||||
new_block->size = block->size - size - sizeof(struct memory_block);
|
||||
new_block->next = block->next;
|
||||
new_block->is_free = 1;
|
||||
|
||||
block->size = size;
|
||||
block->next = new_block;
|
||||
}
|
||||
}
|
||||
|
||||
void mark_as_free(void *ptr) {
|
||||
struct memory_block *block = (struct memory_block *)ptr;
|
||||
block->is_free = 1;
|
||||
|
||||
// Coalesce with next block if it's free
|
||||
if (block->next && block->next->is_free) {
|
||||
block->size += block->next->size + sizeof(struct memory_block);
|
||||
block->next = block->next->next;
|
||||
}
|
||||
|
||||
// Coalesce with previous block if it's free
|
||||
struct memory_block *prev = free_blocks;
|
||||
while (prev && prev->next != block) {
|
||||
prev = prev->next;
|
||||
}
|
||||
if (prev && prev->is_free) {
|
||||
prev->size += block->size + sizeof(struct memory_block);
|
||||
prev->next = block->next;
|
||||
}
|
||||
}
|
||||
|
||||
void *malloc(size_t size)
|
||||
{
|
||||
if (heap_start == NULL || heap_end == NULL)
|
||||
{
|
||||
// Heap not initialized, cannot allocate
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Align the size to the word size for efficiency
|
||||
size = (size + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1);
|
||||
|
||||
// Search for a free block of sufficient size
|
||||
void *block = find_free_block(size);
|
||||
if (block != NULL)
|
||||
{
|
||||
// Mark the block as used
|
||||
mark_as_used(block, size);
|
||||
return (void *)((uintptr_t)block + sizeof(struct memory_block));
|
||||
}
|
||||
|
||||
// No suitable block found, out of memory
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free(void *ptr)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust pointer to the start of the memory block
|
||||
struct memory_block *block = (struct memory_block *)((uintptr_t)ptr - sizeof(struct memory_block));
|
||||
|
||||
// Mark the block as free
|
||||
mark_as_free(block);
|
||||
}
|
27
kernel/malloc.h
Normal file
27
kernel/malloc.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef MALLOC_H
|
||||
#define MALLOC_H
|
||||
|
||||
#include <stddef.h> // For size_t
|
||||
|
||||
// Define the memory block structure
|
||||
struct memory_block {
|
||||
size_t size;
|
||||
struct memory_block *next;
|
||||
int is_free;
|
||||
};
|
||||
|
||||
// Function prototypes
|
||||
void init_heap(void *start, void *end);
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
|
||||
// Helper function prototypes
|
||||
void *find_free_block(size_t size);
|
||||
void mark_as_used(void *ptr, size_t size);
|
||||
void mark_as_free(void *ptr);
|
||||
|
||||
// External heap boundaries
|
||||
extern void *user_heap_start;
|
||||
extern void *user_heap_end;
|
||||
|
||||
#endif // MALLOC_H
|
@@ -1,14 +1,21 @@
|
||||
#include "memmap.h"
|
||||
|
||||
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) {
|
||||
// Fill with dummy values for now
|
||||
map[0].base_addr = 0x00000000;
|
||||
map[0].length = 0x0009FC00;
|
||||
map[0].type = 1;
|
||||
uint32_t count = 0;
|
||||
|
||||
map[1].base_addr = 0x00100000;
|
||||
map[1].length = 0x1FF00000;
|
||||
map[1].type = 1;
|
||||
if (max_entries >= 1) {
|
||||
map[count].base_addr = 0x00000000;
|
||||
map[count].length = 0x0009FC00;
|
||||
map[count].type = 1;
|
||||
count++;
|
||||
}
|
||||
|
||||
return 2; // 2 regions
|
||||
}
|
||||
if (max_entries >= 2) {
|
||||
map[count].base_addr = 0x00100000;
|
||||
map[count].length = 0x1FF00000;
|
||||
map[count].type = 1;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
27
kernel/mouse.c
Normal file
27
kernel/mouse.c
Normal file
@@ -0,0 +1,27 @@
|
||||
// mouse.c
|
||||
#include "mouse.h"
|
||||
#include "usb.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Mouse buffer
|
||||
static mouse_data_t mouse_data;
|
||||
|
||||
// Read USB mouse data
|
||||
mouse_data_t usb_read_mouse(void) {
|
||||
uint8_t buffer[3]; // USB HID Mouse reports typically use 3 bytes
|
||||
if (usb_interrupt_transfer(buffer, sizeof(buffer))) { // Ensure buffer is filled
|
||||
// Process the received data
|
||||
mouse_data.x += buffer[1]; // X movement
|
||||
mouse_data.y += buffer[2]; // Y movement
|
||||
mouse_data.left_button = buffer[0] & 0x01;
|
||||
mouse_data.right_button = buffer[0] & 0x02;
|
||||
} else {
|
||||
// Handle the case where no data was received
|
||||
// You can choose to reset mouse_data or leave it unchanged
|
||||
// For example, you might want to set movement to zero if no data is received
|
||||
// mouse_data.x = 0;
|
||||
// mouse_data.y = 0;
|
||||
}
|
||||
return mouse_data;
|
||||
}
|
26
kernel/mouse.h
Normal file
26
kernel/mouse.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef MOUSE_H
|
||||
#define MOUSE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Mouse data structure
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
bool left_button;
|
||||
bool right_button;
|
||||
} mouse_data_t;
|
||||
|
||||
// Function declarations for USB 1.x HID mouse support
|
||||
bool usb_mouse_init(void);
|
||||
bool usb_mouse_detected(void);
|
||||
bool usb_mouse_received(void);
|
||||
mouse_data_t usb_read_mouse(void);
|
||||
|
||||
// USB Host Controller Interface functions (USB 1.x support)
|
||||
bool uhci_init(void);
|
||||
bool ohci_init(void);
|
||||
|
||||
#endif // MOUSE_H
|
||||
|
0
kernel/multiboot.h
Normal file
0
kernel/multiboot.h
Normal file
@@ -1,21 +1,21 @@
|
||||
#include "paging.h"
|
||||
#include "io.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
page_directory_entry_t *page_directory = (page_directory_entry_t *)0x100000;
|
||||
page_table_entry_t *page_table = (page_table_entry_t *)0x101000; // Located right after the page directory
|
||||
page_table_entry_t *page_table = (page_table_entry_t *)0x101000;
|
||||
page_table_entry_t *heap_page_table = (page_table_entry_t *)0x102000; // Located right after the page directory
|
||||
|
||||
// Helper function to set up the page directory entry
|
||||
void set_page_directory(page_directory_entry_t *dir) {
|
||||
for (int i = 0; i < PAGE_DIRECTORY_SIZE; i++) {
|
||||
// Set up a page directory entry with identity mapping
|
||||
dir[i].present = 1;
|
||||
dir[i].rw = 1; // Read/Write
|
||||
dir[i].user = 0; // Kernel mode
|
||||
dir[i].write_through = 0;
|
||||
dir[i].cache_disabled = 0;
|
||||
dir[i].accessed = 0;
|
||||
dir[i].frame = (uint32_t)&page_table[i] >> 12; // Page table frame address
|
||||
dir[i].present = 0;
|
||||
}
|
||||
dir[0].present = 1;
|
||||
dir[0].rw = 1;
|
||||
dir[0].user = 0;
|
||||
dir[0].frame = (uint32_t)page_table >> 12;
|
||||
}
|
||||
|
||||
// Helper function to set up the page table entry
|
||||
@@ -47,10 +47,26 @@ void enable_paging() {
|
||||
|
||||
// Initialize paging: set up the page directory and enable paging
|
||||
void paging_init() {
|
||||
// Set up the page directory and page tables
|
||||
// Set up identity-mapped page directory + table
|
||||
set_page_directory(page_directory);
|
||||
set_page_table(page_table);
|
||||
|
||||
// Enable paging
|
||||
// === 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();
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#define PAGE_SIZE 4096 // Page size in bytes
|
||||
#define PAGE_DIRECTORY_SIZE 1024 // 1024 entries in page directory
|
||||
#define PAGE_TABLE_SIZE 1024 // 1024 entries in a page table
|
||||
#define KERNEL_HEAP_START 0xC0100000
|
||||
|
||||
// Page Directory and Page Table structure
|
||||
typedef struct {
|
||||
@@ -38,6 +39,7 @@ typedef struct {
|
||||
|
||||
extern page_directory_entry_t *page_directory;
|
||||
extern page_table_entry_t *page_table;
|
||||
extern page_table_entry_t *heap_page_table;
|
||||
|
||||
void paging_init(void);
|
||||
void set_page_directory(page_directory_entry_t *dir);
|
||||
|
19
kernel/panic.c
Normal file
19
kernel/panic.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "panic.h"
|
||||
#include "terminal.h"
|
||||
#include "serial.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
void panic(const char *message) {
|
||||
terminal_write("KERNEL PANIC: ");
|
||||
terminal_write(message);
|
||||
terminal_write("\nSystem halted.\n");
|
||||
|
||||
serial_write("KERNEL PANIC: ");
|
||||
serial_write(message);
|
||||
serial_write("\nSystem halted.\n");
|
||||
|
||||
// Halt the system
|
||||
while (true) {
|
||||
asm volatile ("cli; hlt");
|
||||
}
|
||||
}
|
6
kernel/panic.h
Normal file
6
kernel/panic.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef PANIC_H
|
||||
#define PANIC_H
|
||||
|
||||
void panic(const char *message);
|
||||
|
||||
#endif // PANIC_H
|
84
kernel/print.c
Normal file
84
kernel/print.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "print.h"
|
||||
|
||||
void my_putchar(char ch) {
|
||||
// Write a single character to standard output
|
||||
// In a freestanding environment, you might need to implement this differently
|
||||
// For now, we will use the standard putchar for demonstration
|
||||
// Replace this with your own implementation if needed
|
||||
putchar(ch);
|
||||
}
|
||||
|
||||
void print_string(const char *str) {
|
||||
// Simple implementation to print a string
|
||||
while (*str) {
|
||||
my_putchar(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
void my_printf(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
while (*format) {
|
||||
if (*format == '%') {
|
||||
format++; // Move to the next character after '%'
|
||||
switch (*format) {
|
||||
case 's': { // String
|
||||
char *str = va_arg(args, char *);
|
||||
print_string(str);
|
||||
break;
|
||||
}
|
||||
case 'd': { // Integer
|
||||
int num = va_arg(args, int);
|
||||
char buffer[20]; // Buffer to hold the string representation
|
||||
snprintf(buffer, sizeof(buffer), "%d", num);
|
||||
print_string(buffer);
|
||||
break;
|
||||
}
|
||||
case 'c': { // Character
|
||||
char ch = (char)va_arg(args, int); // Promote char to int
|
||||
my_putchar(ch);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
my_putchar('%'); // Print the '%' if no valid format specifier
|
||||
my_putchar(*format);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
my_putchar(*format);
|
||||
}
|
||||
format++;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
void print_hex(unsigned int num) {
|
||||
// Buffer to hold the hexadecimal representation
|
||||
char buffer[9]; // 8 hex digits + null terminator
|
||||
buffer[8] = '\0'; // Null-terminate the string
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
int digit = num & 0xF; // Get the last 4 bits
|
||||
buffer[i] = (digit < 10) ? (digit + '0') : (digit - 10 + 'A'); // Convert to hex character
|
||||
num >>= 4; // Shift right by 4 bits
|
||||
}
|
||||
|
||||
// Print the buffer, skipping leading zeros
|
||||
int leading_zero = 1;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (buffer[i] != '0') {
|
||||
leading_zero = 0; // Found a non-zero digit
|
||||
}
|
||||
if (!leading_zero) {
|
||||
my_putchar(buffer[i]);
|
||||
}
|
||||
}
|
||||
if (leading_zero) {
|
||||
my_putchar('0'); // If all were zeros, print a single '0'
|
||||
}
|
||||
}
|
9
kernel/print.h
Normal file
9
kernel/print.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef PRINT_H
|
||||
#define PRINT_H
|
||||
|
||||
void print_string(const char *str);
|
||||
void my_printf(const char *format, ...);
|
||||
void print_hex(unsigned int num);
|
||||
void my_putchar(char ch);
|
||||
|
||||
#endif
|
62
kernel/scheduler.c
Normal file
62
kernel/scheduler.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "scheduler.h"
|
||||
#include <stddef.h>
|
||||
|
||||
static task_t tasks[MAX_TASKS];
|
||||
static uint32_t task_stacks[MAX_TASKS][STACK_SIZE / sizeof(uint32_t)];
|
||||
|
||||
static int task_count = 0;
|
||||
static task_t *task_list = NULL;
|
||||
static task_t *current_task = NULL;
|
||||
|
||||
void scheduler_init() {
|
||||
// Initialize task list, etc.
|
||||
task_list = NULL;
|
||||
current_task = NULL;
|
||||
task_count = 0;
|
||||
}
|
||||
|
||||
void scheduler_add_task(void (*entry)(void)) {
|
||||
if (task_count >= MAX_TASKS || entry == NULL) return;
|
||||
|
||||
task_t *new_task = &tasks[task_count];
|
||||
new_task->id = task_count;
|
||||
new_task->entry = entry;
|
||||
|
||||
// Simulate a stack pointer pointing to the "top" of the stack
|
||||
new_task->stack_ptr = &task_stacks[task_count][STACK_SIZE / sizeof(uint32_t) - 1];
|
||||
|
||||
new_task->next = NULL;
|
||||
|
||||
// Add to task list
|
||||
if (task_list == NULL) {
|
||||
task_list = new_task;
|
||||
} else {
|
||||
task_t *tail = task_list;
|
||||
while (tail->next) {
|
||||
tail = tail->next;
|
||||
}
|
||||
tail->next = new_task;
|
||||
}
|
||||
|
||||
task_count++;
|
||||
}
|
||||
|
||||
void scheduler_schedule() {
|
||||
// Very basic round-robin switch
|
||||
if (current_task && current_task->next) {
|
||||
current_task = current_task->next;
|
||||
} else {
|
||||
current_task = task_list; // Loop back
|
||||
}
|
||||
|
||||
// Call context switch or simulate yielding to current_task
|
||||
// In real system: context_switch_to(current_task)
|
||||
if (current_task && current_task->entry) {
|
||||
current_task->entry(); // Simulate switching by calling
|
||||
}
|
||||
}
|
||||
|
||||
void scheduler_yield() {
|
||||
// Stub: manually call schedule for cooperative multitasking
|
||||
scheduler_schedule();
|
||||
}
|
21
kernel/scheduler.h
Normal file
21
kernel/scheduler.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef SCHEDULER_H
|
||||
#define SCHEDULER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_TASKS 8
|
||||
#define STACK_SIZE 1024
|
||||
|
||||
typedef struct task {
|
||||
uint32_t id;
|
||||
void (*entry)(void);
|
||||
uint32_t *stack_ptr;
|
||||
struct task *next;
|
||||
} task_t;
|
||||
|
||||
void scheduler_init();
|
||||
void scheduler_add_task(void (*entry)(void));
|
||||
void scheduler_schedule();
|
||||
void scheduler_yield(); // Optional for cooperative scheduling
|
||||
|
||||
#endif // SCHEDULER_H
|
@@ -1,20 +1,40 @@
|
||||
#include "io.h"
|
||||
#include "serial.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define COM1 0x3F8
|
||||
#define COM2 0x2F8
|
||||
#define COM3 0x3E8
|
||||
#define COM4 0x2E8
|
||||
|
||||
void serial_init_port(uint16_t port) {
|
||||
outb(port + 1, 0x00);
|
||||
outb(port + 3, 0x80);
|
||||
outb(port + 0, 0x03);
|
||||
outb(port + 1, 0x00);
|
||||
outb(port + 3, 0x03);
|
||||
outb(port + 2, 0xC7);
|
||||
outb(port + 4, 0x0B);
|
||||
}
|
||||
|
||||
void serial_init(void) {
|
||||
outb(COM1 + 1, 0x00); // Disable interrupts
|
||||
outb(COM1 + 3, 0x80); // Enable DLAB
|
||||
outb(COM1 + 0, 0x03); // Set baud rate to 38400
|
||||
outb(COM1 + 1, 0x00);
|
||||
outb(COM1 + 3, 0x03); // 8 bits, no parity, one stop bit
|
||||
outb(COM1 + 2, 0xC7); // Enable FIFO, clear, 14-byte threshold
|
||||
outb(COM1 + 4, 0x0B); // IRQs enabled, RTS/DSR set
|
||||
serial_init_port(COM1);
|
||||
serial_init_port(COM2);
|
||||
serial_init_port(COM3);
|
||||
serial_init_port(COM4);
|
||||
}
|
||||
|
||||
void serial_write_char(char c) {
|
||||
while (!(inb(COM1 + 5) & 0x20));
|
||||
outb(COM1, c);
|
||||
}
|
||||
|
||||
void serial_write(const char *str) {
|
||||
while (*str) {
|
||||
while (!(inb(COM1 + 5) & 0x20)); // Wait for the transmitter holding register to be empty
|
||||
outb(COM1, *str++);
|
||||
serial_write_char(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
void serial_write_string(const char* str) {
|
||||
serial_write(str);
|
||||
}
|
||||
|
@@ -4,7 +4,9 @@
|
||||
#include <stdint.h>
|
||||
|
||||
void serial_init(void);
|
||||
void serial_write(char c);
|
||||
void serial_init_port(uint16_t port);
|
||||
void serial_write_char(char c);
|
||||
void serial_write(const char *str);
|
||||
void serial_write_string(const char *str);
|
||||
|
||||
#endif
|
||||
#endif
|
62
kernel/shell.c
Normal file
62
kernel/shell.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "shell.h"
|
||||
#include "keyboard.h"
|
||||
#include "terminal.h"
|
||||
#include "print.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "string_utils.h"
|
||||
|
||||
void execute(char *input) {
|
||||
if (my_strcmp(input, "help") == 0) {
|
||||
my_printf("Available commands: help, clear, exit\n");
|
||||
} else if (my_strcmp(input, "clear") == 0) {
|
||||
terminal_clear();
|
||||
} else {
|
||||
my_printf("Unknown command: %s\n", input);
|
||||
}
|
||||
}
|
||||
|
||||
void shell_loop()
|
||||
{
|
||||
char input[256];
|
||||
size_t index = 0;
|
||||
char c;
|
||||
|
||||
while (1)
|
||||
{
|
||||
my_printf("> ");
|
||||
index = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
c = keyboard_get_char(); // Waits for input
|
||||
|
||||
if (c == '\n' || c == '\r') // Enter key
|
||||
{
|
||||
input[index] = '\0';
|
||||
my_printf("\n");
|
||||
break;
|
||||
}
|
||||
else if (c == '\b' || c == 127) // Backspace
|
||||
{
|
||||
if (index > 0)
|
||||
{
|
||||
index--;
|
||||
my_printf("\b \b"); // Erase last char on screen
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index < sizeof(input) - 1) {
|
||||
input[index++] = c;
|
||||
putchar(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (my_strcmp(input, "exit") == 0)
|
||||
break;
|
||||
|
||||
execute(input);
|
||||
}
|
||||
}
|
7
kernel/shell.h
Normal file
7
kernel/shell.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef SHELL_H
|
||||
#define SHELL_H
|
||||
|
||||
void shell_loop(void);
|
||||
void execute(char *input);
|
||||
|
||||
#endif
|
80
kernel/string_utils.c
Normal file
80
kernel/string_utils.c
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "string_utils.h"
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
size_t my_strlen(const char *str) {
|
||||
const char *s = str;
|
||||
while (*s) s++;
|
||||
return s - str;
|
||||
}
|
||||
|
||||
// Forward declaration of my_itoa
|
||||
static size_t my_itoa(int value, char *str, size_t size);
|
||||
|
||||
int my_vsnprintf(char *str, size_t size, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
size_t written = 0; // Change to size_t
|
||||
|
||||
for (const char *p = format; *p != '\0' && written < size - 1; p++) {
|
||||
if (*p == '%') {
|
||||
p++;
|
||||
if (*p == 's') {
|
||||
const char *s = va_arg(args, const char *);
|
||||
while (*s && written < size - 1) {
|
||||
str[written++] = *s++;
|
||||
}
|
||||
} else if (*p == 'd') {
|
||||
// Handle integer formatting
|
||||
written += my_itoa(va_arg(args, int), str + written, size - written);
|
||||
} else {
|
||||
// Handle other formats as needed
|
||||
str[written++] = *p; // Just copy the character
|
||||
}
|
||||
} else {
|
||||
str[written++] = *p;
|
||||
}
|
||||
}
|
||||
str[written] = '\0'; // Null-terminate the string
|
||||
va_end(args);
|
||||
return written;
|
||||
}
|
||||
|
||||
static size_t my_itoa(int value, char *str, size_t size) {
|
||||
size_t written = 0; // Change to size_t
|
||||
if (value < 0) {
|
||||
if (written < size - 1) {
|
||||
str[written++] = '-';
|
||||
}
|
||||
value = -value;
|
||||
}
|
||||
// Convert integer to string
|
||||
int temp = value;
|
||||
int digits = 0;
|
||||
do {
|
||||
digits++;
|
||||
temp /= 10;
|
||||
} while (temp);
|
||||
|
||||
if (written + digits >= size) {
|
||||
digits = size - written - 1; // Prevent overflow
|
||||
}
|
||||
|
||||
str += written + digits; // Move pointer to the end
|
||||
*str-- = '\0'; // Null-terminate the string
|
||||
|
||||
do {
|
||||
*str-- = (value % 10) + '0';
|
||||
value /= 10;
|
||||
} while (value && str >= str - digits);
|
||||
|
||||
return written + digits; // Return total written characters
|
||||
}
|
||||
|
||||
int my_strcmp(const char *str1, const char *str2) {
|
||||
while (*str1 && (*str1 == *str2)) {
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
return *(unsigned char *)str1 - *(unsigned char *)str2;
|
||||
}
|
11
kernel/string_utils.h
Normal file
11
kernel/string_utils.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef STRING_UTILS_H
|
||||
#define STRING_UTILS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h> // Include for va_list and related macros
|
||||
|
||||
size_t my_strlen(const char *str); // Renamed to avoid conflict
|
||||
int my_vsnprintf(char *str, size_t size, const char *format, ...); // Renamed to avoid conflict
|
||||
int my_strcmp(const char *str1, const char *str2);
|
||||
|
||||
#endif // STRING_UTILS_H
|
29
kernel/syscalls.c
Normal file
29
kernel/syscalls.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "syscalls.h"
|
||||
#include "scheduler.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
void syscall_handler(int code, va_list args) {
|
||||
switch (code) {
|
||||
case SYSCALL_INIT:
|
||||
scheduler_init();
|
||||
break;
|
||||
case SYSCALL_SPAWN: {
|
||||
void (*entry)(void) = va_arg(args, void (*)(void));
|
||||
scheduler_add_task(entry);
|
||||
break;
|
||||
}
|
||||
case SYSCALL_YIELD:
|
||||
scheduler_yield();
|
||||
break;
|
||||
default:
|
||||
// Unknown syscall
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void syscall(int code, ...) {
|
||||
va_list args;
|
||||
va_start(args, code);
|
||||
syscall_handler(code, args);
|
||||
va_end(args);
|
||||
}
|
17
kernel/syscalls.h
Normal file
17
kernel/syscalls.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef SYSCALLS_H
|
||||
#define SYSCALLS_H
|
||||
|
||||
#include <stdarg.h>
|
||||
// Syscall numbers
|
||||
typedef enum {
|
||||
SYSCALL_INIT = 0,
|
||||
SYSCALL_SPAWN,
|
||||
SYSCALL_YIELD
|
||||
} syscall_code_t;
|
||||
|
||||
// Syscall dispatcher
|
||||
void syscall_handler(int code, va_list args);
|
||||
// Syscall interface
|
||||
void syscall(int code, ...);
|
||||
|
||||
#endif // SYSCALLS_H
|
@@ -1,5 +1,7 @@
|
||||
#include <stdint.h>
|
||||
#include "io.h"
|
||||
#include "terminal.h"
|
||||
#include "vga.h"
|
||||
|
||||
#define VGA_ADDRESS 0xB8000
|
||||
#define VGA_WIDTH 80
|
||||
@@ -9,29 +11,41 @@
|
||||
static uint16_t* const vga_buffer = (uint16_t*) VGA_ADDRESS;
|
||||
static uint8_t cursor_x = 0;
|
||||
static uint8_t cursor_y = 0;
|
||||
|
||||
static uint16_t vga_entry(char c, uint8_t color) {
|
||||
return (uint16_t) color << 8 | (uint8_t) c;
|
||||
}
|
||||
static uint8_t current_color = WHITE_ON_BLACK;
|
||||
|
||||
void terminal_initialize(void) {
|
||||
for (uint16_t y = 0; y < VGA_HEIGHT; y++) {
|
||||
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
|
||||
const size_t index = y * VGA_WIDTH + x;
|
||||
vga_buffer[index] = vga_entry(' ', WHITE_ON_BLACK);
|
||||
vga_buffer[index] = vga_entry(' ', current_color);
|
||||
}
|
||||
}
|
||||
cursor_x = 0;
|
||||
cursor_y = 0;
|
||||
update_cursor(); // Optional: good idea to reset position
|
||||
}
|
||||
|
||||
void terminal_putchar(char c) {
|
||||
// Handle backspace
|
||||
if (c == '\b') {
|
||||
if (cursor_x > 0) {
|
||||
cursor_x--;
|
||||
} else if (cursor_y > 0) {
|
||||
cursor_y--;
|
||||
cursor_x = VGA_WIDTH - 1;
|
||||
}
|
||||
vga_buffer[cursor_y * VGA_WIDTH + cursor_x] = vga_entry(' ', current_color);
|
||||
update_cursor(); // Optional, if you add cursor updating
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle newline
|
||||
if (c == '\n') {
|
||||
cursor_x = 0;
|
||||
cursor_y++;
|
||||
} else {
|
||||
const size_t index = cursor_y * VGA_WIDTH + cursor_x;
|
||||
vga_buffer[index] = vga_entry(c, WHITE_ON_BLACK);
|
||||
vga_buffer[index] = vga_entry(c, current_color);
|
||||
cursor_x++;
|
||||
if (cursor_x >= VGA_WIDTH) {
|
||||
cursor_x = 0;
|
||||
@@ -49,15 +63,44 @@ void terminal_putchar(char c) {
|
||||
|
||||
// Clear the last line
|
||||
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
|
||||
vga_buffer[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = vga_entry(' ', WHITE_ON_BLACK);
|
||||
vga_buffer[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = vga_entry(' ', current_color);
|
||||
}
|
||||
|
||||
cursor_y = VGA_HEIGHT - 1;
|
||||
}
|
||||
|
||||
update_cursor(); // Optional, if you want the hardware cursor to follow
|
||||
}
|
||||
|
||||
|
||||
void terminal_write(const char* str) {
|
||||
for (size_t i = 0; str[i] != '\0'; i++) {
|
||||
terminal_putchar(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void terminal_setcolor(uint8_t color)
|
||||
{
|
||||
current_color = color;
|
||||
}
|
||||
|
||||
void terminal_clear(void) {
|
||||
for (uint16_t y = 0; y < VGA_HEIGHT; y++) {
|
||||
for (uint16_t x = 0; x < VGA_WIDTH; x++) {
|
||||
const size_t index = y * VGA_WIDTH + x;
|
||||
vga_buffer[index] = vga_entry(' ', current_color);
|
||||
}
|
||||
}
|
||||
cursor_x = 0;
|
||||
cursor_y = 0;
|
||||
update_cursor();
|
||||
}
|
||||
|
||||
void update_cursor() {
|
||||
uint16_t pos = cursor_y * VGA_WIDTH + cursor_x;
|
||||
|
||||
outb(0x3D4, 0x0F);
|
||||
outb(0x3D5, (uint8_t)(pos & 0xFF));
|
||||
outb(0x3D4, 0x0E);
|
||||
outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF));
|
||||
}
|
||||
|
@@ -7,5 +7,7 @@ void terminal_initialize(void);
|
||||
void terminal_putchar(char c);
|
||||
void terminal_write(const char *str);
|
||||
void terminal_setcolor(uint8_t color);
|
||||
void terminal_clear(void);
|
||||
void update_cursor(void);
|
||||
|
||||
#endif
|
||||
|
118
kernel/threading.c
Normal file
118
kernel/threading.c
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "threading.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_THREADS 16 // Maximum number of threads
|
||||
#define THREAD_STACK_SIZE 8192 // Stack size for each thread
|
||||
|
||||
// The thread table stores information about all threads
|
||||
static Thread thread_table[MAX_THREADS];
|
||||
static uint32_t current_thread = 0; // Index of the currently running thread
|
||||
static uint32_t num_threads = 0; // Number of active threads
|
||||
|
||||
// A simple mutex spinlock
|
||||
static volatile int mutex_locked = 0;
|
||||
|
||||
// Function declaration for context_switch
|
||||
void context_switch(Thread *next);
|
||||
|
||||
// Initialize the threading system
|
||||
void thread_init(void) {
|
||||
memset(thread_table, 0, sizeof(thread_table));
|
||||
num_threads = 0;
|
||||
}
|
||||
|
||||
// Create a new thread
|
||||
void thread_create(Thread *thread __attribute__((unused)), void (*start_routine)(void *), void *arg) {
|
||||
if (num_threads >= MAX_THREADS) {
|
||||
printf("Error: Maximum thread count reached.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find an empty slot for the new thread
|
||||
int index = num_threads++;
|
||||
thread_table[index] = (Thread){0};
|
||||
|
||||
// Set up the new thread
|
||||
thread_table[index].start_routine = start_routine;
|
||||
thread_table[index].arg = arg;
|
||||
thread_table[index].stack_size = THREAD_STACK_SIZE;
|
||||
thread_table[index].stack = (uint32_t*)malloc(THREAD_STACK_SIZE);
|
||||
thread_table[index].stack_top = thread_table[index].stack + THREAD_STACK_SIZE / sizeof(uint32_t);
|
||||
|
||||
// Initialize the stack (simulate pushing the function's return address)
|
||||
uint32_t *stack_top = thread_table[index].stack_top;
|
||||
*(--stack_top) = (uint32_t)start_routine; // Return address (the thread's entry point)
|
||||
*(--stack_top) = (uint32_t)arg; // Argument to pass to the thread
|
||||
|
||||
// Set the thread's state to ready
|
||||
thread_table[index].state = THREAD_READY;
|
||||
|
||||
// If this is the first thread, switch to it
|
||||
if (index == 0) {
|
||||
scheduler();
|
||||
}
|
||||
}
|
||||
|
||||
// Yield the CPU to another thread
|
||||
void thread_yield(void) {
|
||||
// Find the next thread in a round-robin manner
|
||||
uint32_t next_thread = (current_thread + 1) % num_threads;
|
||||
while (next_thread != current_thread && thread_table[next_thread].state != THREAD_READY) {
|
||||
next_thread = (next_thread + 1) % num_threads;
|
||||
}
|
||||
|
||||
if (next_thread != current_thread) {
|
||||
current_thread = next_thread;
|
||||
scheduler();
|
||||
}
|
||||
}
|
||||
|
||||
// Exit the current thread
|
||||
void thread_exit(void) {
|
||||
thread_table[current_thread].state = THREAD_BLOCKED; // Mark the thread as blocked (finished)
|
||||
free(thread_table[current_thread].stack); // Free the thread's stack
|
||||
num_threads--; // Decrease thread count
|
||||
|
||||
// Yield to the next thread
|
||||
thread_yield();
|
||||
}
|
||||
|
||||
// Scheduler: This function selects the next thread to run
|
||||
void scheduler(void) {
|
||||
// Find the next ready thread
|
||||
uint32_t next_thread = (current_thread + 1) % num_threads;
|
||||
while (thread_table[next_thread].state != THREAD_READY) {
|
||||
next_thread = (next_thread + 1) % num_threads;
|
||||
}
|
||||
|
||||
if (next_thread != current_thread) {
|
||||
current_thread = next_thread;
|
||||
context_switch(&thread_table[current_thread]);
|
||||
}
|
||||
}
|
||||
|
||||
// Context switch to the next thread (assembly would go here to save/load registers)
|
||||
void context_switch(Thread *next) {
|
||||
// For simplicity, context switching in this example would involve saving/restoring registers.
|
||||
// In a real system, you would need to save the CPU state (registers) and restore the next thread's state.
|
||||
printf("Switching to thread...\n");
|
||||
next->start_routine(next->arg); // Start running the next thread
|
||||
}
|
||||
|
||||
// Simple mutex functions (spinlock)
|
||||
void mutex_init(void) {
|
||||
mutex_locked = 0;
|
||||
}
|
||||
|
||||
void mutex_lock(void) {
|
||||
while (__sync_lock_test_and_set(&mutex_locked, 1)) {
|
||||
// Busy wait (spinlock)
|
||||
}
|
||||
}
|
||||
|
||||
void mutex_unlock(void) {
|
||||
__sync_lock_release(&mutex_locked);
|
||||
}
|
36
kernel/threading.h
Normal file
36
kernel/threading.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef THREADING_H
|
||||
#define THREADING_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Define a basic thread structure (Thread Control Block - TCB)
|
||||
typedef struct Thread {
|
||||
void (*start_routine)(void *); // Function pointer to the thread function
|
||||
void *arg; // Argument to be passed to the thread function
|
||||
uint32_t *stack; // Pointer to the thread's stack
|
||||
uint32_t *stack_top; // Top of the stack (where the thread will start)
|
||||
uint32_t stack_size; // Size of the stack
|
||||
uint32_t state; // Thread state (running, ready, blocked)
|
||||
} Thread;
|
||||
|
||||
// Thread states
|
||||
#define THREAD_RUNNING 0
|
||||
#define THREAD_READY 1
|
||||
#define THREAD_BLOCKED 2
|
||||
|
||||
// Thread management functions
|
||||
void thread_init(void);
|
||||
void thread_create(Thread *thread, void (*start_routine)(void *), void *arg);
|
||||
void thread_yield(void);
|
||||
void thread_exit(void);
|
||||
|
||||
// Scheduler function
|
||||
void scheduler(void);
|
||||
|
||||
// Synchronization functions (mutex spinlocks)
|
||||
void mutex_init(void);
|
||||
void mutex_lock(void);
|
||||
void mutex_unlock(void);
|
||||
|
||||
#endif // THREADING_H
|
35
kernel/timer.c
Normal file
35
kernel/timer.c
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "timer.h"
|
||||
#include "io.h"
|
||||
#include "isr.h"
|
||||
#include "terminal.h"
|
||||
#include "stdio.h"
|
||||
|
||||
static volatile uint32_t tick = 0;
|
||||
|
||||
void timer_callback(void) {
|
||||
tick++;
|
||||
|
||||
if (tick % 100 == 0) {
|
||||
char buf[16];
|
||||
itoa(tick, buf, 10);
|
||||
terminal_write("Tick count: ");
|
||||
terminal_write(buf);
|
||||
terminal_write("\n");
|
||||
}
|
||||
|
||||
outb(0x20, 0x20); // EOI to PIC
|
||||
}
|
||||
|
||||
void timer_init(uint32_t frequency) {
|
||||
register_interrupt_handler(32, timer_callback); // IRQ0 = Interrupt 32
|
||||
|
||||
uint32_t divisor = 1193180 / frequency;
|
||||
|
||||
outb(0x43, 0x36); // Command byte
|
||||
outb(0x40, divisor & 0xFF); // Low byte
|
||||
outb(0x40, (divisor >> 8)); // High byte
|
||||
}
|
||||
|
||||
uint32_t timer_get_ticks(void) {
|
||||
return tick;
|
||||
}
|
9
kernel/timer.h
Normal file
9
kernel/timer.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void timer_init(uint32_t frequency);
|
||||
uint32_t timer_get_ticks(void);
|
||||
|
||||
#endif
|
9
kernel/types.c
Normal file
9
kernel/types.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "types.h"
|
||||
|
||||
// Example: Basic memory helper (unnecessary if libc exists)
|
||||
void *memset(void *dest, int value, size_t len) {
|
||||
unsigned char *ptr = (unsigned char *)dest;
|
||||
while (len-- > 0)
|
||||
*ptr++ = (unsigned char)value;
|
||||
return dest;
|
||||
}
|
62
kernel/types.h
Normal file
62
kernel/types.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
// ----------------------------
|
||||
// Fixed-width integer types
|
||||
// ----------------------------
|
||||
typedef unsigned char uint8_t;
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef signed short int16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef signed long long int64_t;
|
||||
|
||||
// ----------------------------
|
||||
// Boolean & NULL definitions
|
||||
// ----------------------------
|
||||
#ifndef __cplusplus
|
||||
typedef enum { false = 0, true = 1 } bool;
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*)0)
|
||||
#endif
|
||||
|
||||
// ----------------------------
|
||||
// OS subsystem types
|
||||
// ----------------------------
|
||||
typedef uint32_t size_t;
|
||||
typedef int32_t ssize_t;
|
||||
|
||||
typedef uint32_t phys_addr_t; // Physical address
|
||||
typedef uint32_t virt_addr_t; // Virtual address
|
||||
|
||||
typedef uint32_t pid_t; // Process ID
|
||||
typedef uint32_t tid_t; // Thread ID
|
||||
|
||||
// ----------------------------
|
||||
// Bitfield & utility macros
|
||||
// ----------------------------
|
||||
#define BIT(n) (1U << (n))
|
||||
#define BITS(m, n) (((1U << ((n) - (m) + 1)) - 1) << (m))
|
||||
|
||||
// Align value to next multiple of alignment
|
||||
#define ALIGN_UP(val, align) (((val) + ((align)-1)) & ~((align)-1))
|
||||
#define ALIGN_DOWN(val, align) ((val) & ~((align)-1))
|
||||
|
||||
// ----------------------------
|
||||
// Attributes for structures
|
||||
// ----------------------------
|
||||
#define PACKED __attribute__((packed))
|
||||
#define ALIGN(x) __attribute__((aligned(x)))
|
||||
|
||||
// ----------------------------
|
||||
// Likely/unlikely branch hints
|
||||
// (for future optimization use)
|
||||
// ----------------------------
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
#endif // TYPES_H
|
64
kernel/usb.c
Normal file
64
kernel/usb.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "usb.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// USB version detection
|
||||
bool usb_detect_version(uint16_t *version) {
|
||||
if (!version) return false;
|
||||
*version = 0x0110; // Example: USB 1.1
|
||||
return true;
|
||||
}
|
||||
|
||||
// USB initialization for 1.x
|
||||
bool usb_init(void) {
|
||||
// Detect controller type (UHCI or OHCI)
|
||||
bool uhci_supported = uhci_init();
|
||||
bool ohci_supported = ohci_init();
|
||||
|
||||
if (!uhci_supported && !ohci_supported) {
|
||||
return false; // No supported controllers found
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// USB device enumeration (1.x)
|
||||
bool usb_enumerate_devices(void) {
|
||||
// Implementation for detecting devices on USB 1.x ports
|
||||
return true;
|
||||
}
|
||||
|
||||
// HID initialization for USB 1.x
|
||||
bool usb_hid_init(void) {
|
||||
// Ensure USB is initialized
|
||||
if (!usb_init()) return false;
|
||||
|
||||
return usb_enumerate_devices();
|
||||
}
|
||||
|
||||
// USB transfers (stubs for 1.x)
|
||||
bool usb_control_transfer(/* parameters */) {
|
||||
// Implement control transfer for USB 1.x
|
||||
return true;
|
||||
}
|
||||
|
||||
bool usb_interrupt_transfer(/* parameters */) {
|
||||
// Implement interrupt transfer for USB 1.x
|
||||
return true;
|
||||
}
|
||||
|
||||
bool usb_bulk_transfer(/* parameters */) {
|
||||
// Implement bulk transfer for USB 1.x
|
||||
return true;
|
||||
}
|
||||
|
||||
// USB host controller initialization (UHCI & OHCI)
|
||||
bool uhci_init(void) {
|
||||
// Initialize UHCI controller (USB 1.x)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ohci_init(void) {
|
||||
// Initialize OHCI controller (USB 1.x)
|
||||
return true;
|
||||
}
|
24
kernel/usb.h
Normal file
24
kernel/usb.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef USB_H
|
||||
#define USB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// USB initialization and management functions
|
||||
bool usb_init(void);
|
||||
bool usb_detect_version(uint16_t *version);
|
||||
bool usb_enumerate_devices(void);
|
||||
|
||||
// HID-specific functions
|
||||
bool usb_hid_init(void);
|
||||
|
||||
// USB transfer functions
|
||||
bool usb_control_transfer(/* parameters */);
|
||||
bool usb_interrupt_transfer(/* parameters */);
|
||||
bool usb_bulk_transfer(/* parameters */);
|
||||
|
||||
// USB host controller initialization (USB 1.x only)
|
||||
bool uhci_init(void); // UHCI support
|
||||
bool ohci_init(void); // OHCI support
|
||||
|
||||
#endif // USB_H
|
91
kernel/utils.c
Normal file
91
kernel/utils.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "utils.h"
|
||||
#include "serial.h"
|
||||
#include "terminal.h"
|
||||
|
||||
static void reverse(char* str, int len) {
|
||||
int start = 0;
|
||||
int end = len - 1;
|
||||
while (start < end) {
|
||||
char temp = str[start];
|
||||
str[start++] = str[end];
|
||||
str[end--] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
// Integer to ASCII for signed ints
|
||||
char* itoa(int value, char* str, int base) {
|
||||
int i = 0;
|
||||
int isNegative = 0;
|
||||
unsigned int uval;
|
||||
|
||||
if (base < 2 || base > 36) {
|
||||
str[0] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
// Handle zero explicitly
|
||||
if (value == 0) {
|
||||
str[i++] = '0';
|
||||
str[i] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
// Handle negative numbers (only for base 10)
|
||||
if (value < 0 && base == 10) {
|
||||
isNegative = 1;
|
||||
uval = (unsigned int)(-value);
|
||||
} else {
|
||||
uval = (unsigned int)value;
|
||||
}
|
||||
|
||||
while (uval != 0) {
|
||||
int rem = uval % base;
|
||||
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
|
||||
uval /= base;
|
||||
}
|
||||
|
||||
if (isNegative) {
|
||||
str[i++] = '-';
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
reverse(str, i);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Integer to ASCII for unsigned ints
|
||||
char* utoa(unsigned int value, char* str, int base) {
|
||||
int i = 0;
|
||||
|
||||
if (base < 2 || base > 36) {
|
||||
str[0] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
if (value == 0) {
|
||||
str[i++] = '0';
|
||||
str[i] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
while (value != 0) {
|
||||
int rem = value % base;
|
||||
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
|
||||
value /= base;
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
reverse(str, i);
|
||||
return str;
|
||||
}
|
||||
|
||||
void print_hex(uint32_t val) {
|
||||
char hex_chars[] = "0123456789ABCDEF";
|
||||
char buf[11] = "0x00000000";
|
||||
for (int i = 9; i >= 2; i--) {
|
||||
buf[i] = hex_chars[val & 0xF];
|
||||
val >>= 4;
|
||||
}
|
||||
terminal_write(buf);
|
||||
serial_write(buf);
|
||||
}
|
14
kernel/utils.h
Normal file
14
kernel/utils.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// Convert integer to string (base is typically 10, 16, etc.)
|
||||
char* itoa(int value, char* str, int base);
|
||||
|
||||
// Convert unsigned integer to string (base is typically 10, 16, etc.)
|
||||
char* utoa(unsigned int value, char* str, int base);
|
||||
|
||||
void print_hex(uint32_t val);
|
||||
|
||||
#endif // UTILS_H
|
151
kernel/vga.c
Normal file
151
kernel/vga.c
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "vga.h"
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "string_utils.h"
|
||||
|
||||
void outb(uint16_t port, uint8_t value) {
|
||||
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
// Read a byte from the specified port
|
||||
uint8_t inb(uint16_t port) {
|
||||
uint8_t value;
|
||||
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
uint8_t vga_entry_color(vga_color fg, vga_color bg) {
|
||||
return fg | bg << 4;
|
||||
}
|
||||
|
||||
uint16_t vga_entry(unsigned char uc, uint8_t color) {
|
||||
return (uint16_t)uc | (uint16_t)color << 8;
|
||||
}
|
||||
|
||||
void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y) {
|
||||
const size_t index = y * 80 + x;
|
||||
((uint16_t*) 0xB8000)[index] = vga_entry(c, color);
|
||||
}
|
||||
|
||||
void vga_clear(uint8_t color) {
|
||||
uint16_t blank = vga_entry(' ', color);
|
||||
for (size_t i = 0; i < 80 * 25; ++i) {
|
||||
((uint16_t*) 0xB8000)[i] = blank;
|
||||
}
|
||||
}
|
||||
|
||||
void vga_write_string(const char* data, size_t size) {
|
||||
size_t x = 0;
|
||||
size_t y = 0;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
if (x >= 80) { // If we reach the end of a line, move to the next line
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
|
||||
if (y >= 25) { // If we reach the bottom of the screen, scroll
|
||||
vga_scroll();
|
||||
y = 24; // Reset to the last row
|
||||
}
|
||||
|
||||
vga_put_entry_at(data[i], vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK), x, y);
|
||||
x++; // Move to the next column
|
||||
}
|
||||
}
|
||||
|
||||
void vga_scroll(void) {
|
||||
uint16_t* video_memory = (uint16_t*)0xB8000;
|
||||
// Shift all lines up by one row (80 columns per row)
|
||||
for (size_t i = 0; i < 24 * 80; ++i) {
|
||||
video_memory[i] = video_memory[i + 80]; // Move one row up
|
||||
}
|
||||
|
||||
// Clear the last row (bottom row)
|
||||
for (size_t i = 24 * 80; i < 25 * 80; ++i) {
|
||||
video_memory[i] = vga_entry(' ', vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_WHITE));
|
||||
}
|
||||
}
|
||||
|
||||
void vga_set_cursor_position(size_t x, size_t y) {
|
||||
uint16_t position = y * 80 + x; // Calculate linear index (y * 80 + x)
|
||||
|
||||
// Set the high byte of the cursor position
|
||||
outb(VGA_PORT_INDEX, 0x0E); // Cursor high byte register
|
||||
outb(VGA_PORT_DATA, position >> 8);
|
||||
|
||||
// Set the low byte of the cursor position
|
||||
outb(VGA_PORT_INDEX, 0x0F); // Cursor low byte register
|
||||
outb(VGA_PORT_DATA, position & 0xFF);
|
||||
}
|
||||
|
||||
size_t vga_get_cursor_position(void) {
|
||||
outb(VGA_PORT_INDEX, 0x0E); // Cursor high byte register
|
||||
uint8_t high = inb(VGA_PORT_DATA); // Read high byte
|
||||
|
||||
outb(VGA_PORT_INDEX, 0x0F); // Cursor low byte register
|
||||
uint8_t low = inb(VGA_PORT_DATA); // Read low byte
|
||||
|
||||
return (high << 8) | low; // Combine the high and low bytes
|
||||
}
|
||||
|
||||
void vga_set_cursor_blinking(bool enable) {
|
||||
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
|
||||
uint8_t cursor = inb(VGA_PORT_DATA);
|
||||
if (enable) {
|
||||
cursor |= 0x01; // Enable blinking
|
||||
} else {
|
||||
cursor &= ~0x01; // Disable blinking
|
||||
}
|
||||
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
|
||||
outb(VGA_PORT_DATA, cursor);
|
||||
}
|
||||
|
||||
void vga_set_cursor_shape(uint8_t start, uint8_t end) {
|
||||
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
|
||||
uint8_t cursor = inb(VGA_PORT_DATA);
|
||||
cursor = (cursor & 0xC0) | (start & 0x3F); // Set start of cursor shape
|
||||
outb(VGA_PORT_DATA, cursor);
|
||||
|
||||
outb(VGA_PORT_INDEX, 0x0B); // Cursor start register
|
||||
outb(VGA_PORT_DATA, end & 0x3F); // Set end of cursor shape
|
||||
}
|
||||
|
||||
void vga_set_cursor_color(uint8_t color) {
|
||||
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
|
||||
uint8_t cursor = inb(VGA_PORT_DATA);
|
||||
cursor = (cursor & 0xC0) | (color & 0x3F); // Set cursor color
|
||||
outb(VGA_PORT_DATA, cursor);
|
||||
}
|
||||
|
||||
void vga_set_cursor_blink_rate(uint8_t rate) {
|
||||
outb(VGA_PORT_INDEX, 0x0A); // Cursor control register
|
||||
uint8_t cursor = inb(VGA_PORT_DATA);
|
||||
cursor = (cursor & 0xC0) | (rate & 0x3F); // Set cursor blink rate
|
||||
outb(VGA_PORT_DATA, cursor);
|
||||
}
|
||||
|
||||
void vga_printf(const char* format, ...) {
|
||||
char buffer[256]; // Buffer to store the formatted string
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
my_vsnprintf(buffer, sizeof(buffer), format, args); // Use my_vsnprintf instead of vsnprintf
|
||||
va_end(args);
|
||||
|
||||
// Now you can use the buffer with vga_write_string
|
||||
vga_write_string(buffer, my_strlen(buffer)); // Use my_strlen instead of strlen
|
||||
}
|
||||
|
||||
void vga_init(void) {
|
||||
// Clear the screen
|
||||
vga_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY));
|
||||
|
||||
// Set the cursor to the top-left corner
|
||||
vga_set_cursor_position(0, 0);
|
||||
|
||||
// Optionally, set cursor blinking and shape
|
||||
vga_set_cursor_blinking(true);
|
||||
vga_set_cursor_shape(0x20, 0x3F); // Default shape
|
||||
}
|
||||
|
53
kernel/vga.h
Normal file
53
kernel/vga.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef VGA_H
|
||||
#define VGA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h> // For va_list, va_start, etc.
|
||||
|
||||
// VGA color definitions
|
||||
typedef enum {
|
||||
VGA_COLOR_BLACK = 0,
|
||||
VGA_COLOR_BLUE = 1,
|
||||
VGA_COLOR_GREEN = 2,
|
||||
VGA_COLOR_CYAN = 3,
|
||||
VGA_COLOR_RED = 4,
|
||||
VGA_COLOR_MAGENTA = 5,
|
||||
VGA_COLOR_BROWN = 6,
|
||||
VGA_COLOR_LIGHT_GREY = 7,
|
||||
VGA_COLOR_DARK_GREY = 8,
|
||||
VGA_COLOR_LIGHT_BLUE = 9,
|
||||
VGA_COLOR_LIGHT_GREEN = 10,
|
||||
VGA_COLOR_LIGHT_CYAN = 11,
|
||||
VGA_COLOR_LIGHT_RED = 12,
|
||||
VGA_COLOR_LIGHT_MAGENTA = 13,
|
||||
VGA_COLOR_LIGHT_BROWN = 14,
|
||||
VGA_COLOR_WHITE = 15,
|
||||
} vga_color;
|
||||
|
||||
// VGA port addresses
|
||||
typedef enum {
|
||||
VGA_PORT_INDEX = 0x3D4, // Index register for VGA
|
||||
VGA_PORT_DATA = 0x3D5 // Data register for VGA
|
||||
} vga_io_port_t;
|
||||
|
||||
// Function prototypes
|
||||
uint8_t vga_entry_color(vga_color fg, vga_color bg);
|
||||
uint16_t vga_entry(unsigned char uc, uint8_t color);
|
||||
|
||||
void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y);
|
||||
void vga_clear(uint8_t color);
|
||||
void vga_write_string(const char* data, size_t size);
|
||||
void vga_scroll(void);
|
||||
void vga_set_cursor_position(size_t x, size_t y);
|
||||
size_t vga_get_cursor_position(void);
|
||||
|
||||
void vga_set_cursor_blinking(bool enable);
|
||||
void vga_set_cursor_shape(uint8_t start, uint8_t end);
|
||||
void vga_set_cursor_color(uint8_t color);
|
||||
void vga_set_cursor_blink_rate(uint8_t rate);
|
||||
|
||||
void vga_printf(const char* format, ...);
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user