1 Commits

Author SHA1 Message Date
23cb50a73f Create ata.h
implement the base ata pio mode driver so that the filesystems like fat16, fat32 work. This is the header for that. It will need a iso9660 driver for cdrom etc optical media
2026-01-18 17:44:18 -08:00
25 changed files with 186 additions and 973 deletions

View File

@@ -8,13 +8,8 @@ OBJCOPY = i386-elf-objcopy
BUILD_DIR = build BUILD_DIR = build
CROSS_DIR = cross CROSS_DIR = cross
DISK_IMG = $(BUILD_DIR)/disk.img DISK_IMG = $(BUILD_DIR)/disk.img
STAGE2_ADDR = 0x7e00
STAGE2_SIZE = 2048 STAGE2_SIZE = 2048
# Place the memory map (e820) past stage2 bl in memory
MEMMAP_BASE = $(shell echo $$(($(STAGE2_ADDR) + $(STAGE2_SIZE))))
KERNEL_C_SRC = $(wildcard kernel/*.c) KERNEL_C_SRC = $(wildcard kernel/*.c)
KERNEL_ASM_SRC = $(wildcard kernel/*.asm) KERNEL_ASM_SRC = $(wildcard kernel/*.asm)
KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC)) KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC))
@@ -34,7 +29,7 @@ stage1: $(BUILD_DIR)
# NOTE: Stage2 final size should be checked against `$(STAGE2_SIZE)` by the build system to avoid an overflow. # NOTE: Stage2 final size should be checked against `$(STAGE2_SIZE)` by the build system to avoid an overflow.
# Alternatively, convey the final stage2 size through other means to stage1. # Alternatively, convey the final stage2 size through other means to stage1.
stage2: $(BUILD_DIR) stage2: $(BUILD_DIR)
$(AS) $(ASFLAGS) -DMEMMAP_BASE=$(MEMMAP_BASE) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm $(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c
$(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o $(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o
$(OBJCOPY) -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin $(OBJCOPY) -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin
@@ -44,7 +39,7 @@ $(BUILD_DIR)/asm_%.o: kernel/%.asm
$(AS) $(ASFLAGS) -o $@ $< $(AS) $(ASFLAGS) -o $@ $<
$(BUILD_DIR)/%.o: kernel/%.c $(BUILD_DIR)/%.o: kernel/%.c
$(CC) -DMEMMAP_BASE=$(MEMMAP_BASE) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $< $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $<
$(BUILD_DIR)/klibc/%.o: klibc/src/%.c $(BUILD_DIR)/klibc/%.o: klibc/src/%.c
$(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $< $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $<

View File

@@ -11,16 +11,16 @@ Bootloader documentation for ClassicOS
## Stage 1 (`stage1.asm`) ## Stage 1 (`stage1.asm`)
Responsible for loading the second stage using BIOS routines, and switching to protected mode.
- Queries CHS parameters from BIOS - Queries CHS parameters from BIOS
- Loads the second stage bootloader (2048 B) to `0x7c00` - Loads the second stage bootloader (2048 B) to `0x7c00`
- Sets up a GDT with descriptor entries for code and data both covering the whole 32-bit address space
- Enables A20 - Enables A20
- Jumps to stage2 - Set CR0.PE (enable protected mode) and jump to stage 2
## Stage 2 (`stage2.asm, stage2_load.c`) ## Stage 2 (`stage2.asm, stage2_load.c`)
- Read and store E820 memory map from BIOS
- Sets up a GDT with descriptor entries for code and data both covering the whole 32-bit address space
- Set CR0.PE (enable protected mode)
- Set up segment registers - Set up segment registers
- Load the kernel ELF header - Load the kernel ELF header
- Parse the program headers, and load all `PT_LOAD` segments from disk - Parse the program headers, and load all `PT_LOAD` segments from disk

View File

@@ -40,8 +40,11 @@ _start:
call enable_a20 call enable_a20
jc a20_error ; Jump if A20 enable fails jc a20_error ; Jump if A20 enable fails
; Jump to s2 ; Setup Global Descriptor Table
jmp 0x7e00 call setup_gdt
; Switch to protected mode and jump to second stage at 0x08:0x7E00
call switch_to_pm
disk_error: disk_error:
mov si, disk_error_msg mov si, disk_error_msg
@@ -238,6 +241,30 @@ check_a20:
clc ; Clear carry flag to indicate success clc ; Clear carry flag to indicate success
ret ret
; ----------------------------------------------------------------
gdt_start:
dq 0x0000000000000000 ; Null descriptor
dq 0x00CF9A000000FFFF ; 32-bit code segment (selector 0x08)
dq 0x00CF92000000FFFF ; 32-bit data segment (selector 0x10)
dq 0x00009A000000FFFF ; 16-bit code segment for real mode (selector 0x18)
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
gdt_end:
setup_gdt:
lgdt [gdt_descriptor]
ret
; ----------------------------------------------------------------
switch_to_pm:
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:0x7E00 ; jump to S2
; ---------------------------------------------------------------- ; ----------------------------------------------------------------
print_string_16: print_string_16:
.loop: .loop:

View File

@@ -1,80 +1,10 @@
[BITS 32]
global _start global _start
global ata_lba_read
extern load_kernel extern load_kernel
%define e820_magic 0x534d4150 ; "SMAP"
%define e820_entry_size 24
%define e820_max_entries 128
; ----------------------------------------------------------------
; Real mode
; ----------------------------------------------------------------
[BITS 16]
_start: _start:
call read_e820
call setup_gdt
call switch_to_pm
read_e820:
xor ebx, ebx
mov es, bx
mov di, MEMMAP_BASE+4 ; ES=0 DI=MEMMAP_BASE+4
xor bp, bp ; Keeping count in bp
.e820_loop:
mov eax, 0xe820
mov ecx, e820_entry_size
mov edx, e820_magic
int 0x15
jc .done ; Error?
cmp eax, e820_magic ; Verify "SMAP"
jne .done
test ecx, ecx ; Skip 0-sized entries
jz .skip
add di, e820_entry_size ; Advance write addr
inc bp ; Increment count
cmp bp, e820_max_entries ; Stop if we're at capacity
jae .done
.skip:
test ebx, ebx
jne .e820_loop
.done:
mov [MEMMAP_BASE], bp ; Store count
ret
setup_gdt:
lgdt [gdt_descriptor]
ret
switch_to_pm:
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:pm_entry
e820_count:
dw 0
gdt_start:
dq 0x0000000000000000 ; Null descriptor
dq 0x00CF9A000000FFFF ; 32-bit code segment (selector 0x08)
dq 0x00CF92000000FFFF ; 32-bit data segment (selector 0x10)
dq 0x00009A000000FFFF ; 16-bit code segment for real mode (selector 0x18)
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
gdt_end:
; ----------------------------------------------------------------
; Protected mode
; ----------------------------------------------------------------
[BITS 32]
pm_entry:
; Set up segments ; Set up segments
; Data segments ; Data segments
mov ax, 0x10 mov ax, 0x10
@@ -88,8 +18,9 @@ pm_entry:
mov ax, 0x08 mov ax, 0x08
mov cs, ax mov cs, ax
; Stack ; Stack (must be identity-mapped)
mov esp, 0x90000 mov esp, 0x90000
call load_kernel call load_kernel
jmp eax jmp eax

View File

@@ -1,119 +0,0 @@
#include "ata.h"
#include "io.h"
#include "print.h"
#define ATA_TIMEOUT 100000
static inline void ata_delay(void) {
/* 400ns delay by reading alternate status */
inb(ATA_PRIMARY_CTRL);
inb(ATA_PRIMARY_CTRL);
inb(ATA_PRIMARY_CTRL);
inb(ATA_PRIMARY_CTRL);
}
bool ata_wait_ready(void) {
for (int i = 0; i < ATA_TIMEOUT; i++) {
uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS);
/* Must NOT be busy AND must be ready */
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
return true;
}
return false;
}
static bool ata_wait(uint8_t mask) {
for (int i = 0; i < ATA_TIMEOUT; i++) {
uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS);
/* If ERR is set, stop waiting and return failure */
if (status & ATA_SR_ERR) return false;
if (!(status & ATA_SR_BSY) && (status & mask))
return true;
}
return false;
}
bool ata_init(void) {
/* Select drive */
outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, ATA_MASTER);
ata_delay();
/* Check if drive exists */
uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS);
if (status == 0xFF || status == 0) return false;
outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
ata_delay();
if (!ata_wait(ATA_SR_DRQ))
return false;
uint16_t identify[256];
for (int i = 0; i < 256; i++)
identify[i] = inw(ATA_PRIMARY_IO);
print_string("[ATA] Primary master detected\n");
return true;
}
bool ata_read_sector(uint32_t lba, uint8_t* buffer) {
if (!buffer) return false;
/* 1. Wait for drive to be ready for command */
if (!ata_wait_ready()) return false;
/* 2. Setup Task File (LBA28) */
outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F));
outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1);
outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba));
outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16));
/* 3. Issue Read Command */
outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
/* 4. Wait for Data Request (DRQ) */
if (!ata_wait(ATA_SR_DRQ))
return false;
/* 5. Transfer data */
for (int i = 0; i < 256; i++) {
uint16_t data = inw(ATA_PRIMARY_IO);
buffer[i * 2] = data & 0xFF;
buffer[i * 2 + 1] = (data >> 8) & 0xFF;
}
ata_delay();
return true;
}
bool ata_write_sector(uint32_t lba, const uint8_t* buffer) {
if (!buffer) return false;
/* 1. Wait for drive to be ready for command */
if (!ata_wait_ready()) return false;
/* 2. Setup Task File */
outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F));
outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1);
outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba));
outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16));
/* 3. Issue Write Command */
outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
/* 4. Wait for drive to request data */
if (!ata_wait(ATA_SR_DRQ))
return false;
/* 5. Transfer data */
for (int i = 0; i < 256; i++) {
uint16_t word = buffer[i * 2] | (buffer[i * 2 + 1] << 8);
outw(ATA_PRIMARY_IO, word);
}
ata_delay();
return true;
}

View File

@@ -32,13 +32,12 @@
#define ATA_SR_ERR 0x01 #define ATA_SR_ERR 0x01
/* Drive select */ /* Drive select */
#define ATA_MASTER 0xA0 #define ATA_MASTER 0x00
#define ATA_SLAVE 0xB0 #define ATA_SLAVE 0x10
/* Public API */ /* Public API */
bool ata_init(void); bool ata_init(void);
bool ata_read_sector(uint32_t lba, uint8_t* buffer); bool ata_read_sector(uint32_t lba, uint8_t* buffer);
bool ata_write_sector(uint32_t lba, const uint8_t* buffer); bool ata_write_sector(uint32_t lba, const uint8_t* buffer);
bool ata_wait_ready(void);
#endif #endif

View File

@@ -1,80 +1,36 @@
#include <string.h>
#include "display.h" #include "display.h"
#include "io.h" #include "io.h" // Include your I/O header for port access
#include "vga.h" #include "vga.h"
// Initialize the display // Initialize the display
void init_display(void) { void init_display(void) {
// Initialize the VGA driver. This typically sets up the 80x25 text mode, // Initialize VGA settings, if necessary
// clears the screen, and sets the cursor. // This could involve setting up the VGA mode, etc.
vga_init(); set_display_mode(0x13); // Example: Set to 320x200 256-color mode
} }
// Enumerate connected displays // Enumerate connected displays
void enumerate_displays(void) { void enumerate_displays(void) {
// This function is often a complex operation in a real driver. // This is a simplified example. Actual enumeration may require
// In this simplified kernel/VGA text mode environment, we use printf // reading from specific VGA registers or using BIOS interrupts.
// to output a message and rely on the fact that VGA is present.
// Clear the display before printing a message // For demonstration, we will just print a message
vga_clear(vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK)); // In a real driver, you would check the VGA registers
// to determine connected displays.
// Output a simplified enumeration message clear_display();
vga_printf("Display: Standard VGA Text Mode (80x25) Detected.\n"); // Here you would typically read from VGA registers to find connected displays
// For example, using inb() to read from VGA ports
// In a real driver, you would use inb() and outb() with specific VGA ports
// to read information (e.g., from the CRTC registers 0x3D4/0x3D5)
// to check for display presence or configuration.
} }
// Set the display mode // Set the display mode
// NOTE: Setting arbitrary VGA modes (like 0x13 for 320x200) is very complex
// and requires writing hundreds of register values, often done via BIOS in
// real mode. Since we are in protected mode and have a simple text driver,
// this function is kept simple or treated as a placeholder for full mode changes.
void set_display_mode(uint8_t mode) { void set_display_mode(uint8_t mode) {
// Check if the requested mode is a known mode (e.g., VGA Text Mode 3) // Set the VGA mode by writing to the appropriate registers
// For this example, we simply acknowledge the call. outb(VGA_PORT, mode); // Example function to write to a port
// A true mode set would involve complex register sequencing.
// The provided vga.c is a Text Mode driver, so a graphical mode set
// like 0x13 (320x200 256-color) would break the existing vga_printf functionality.
// A simplified text-mode-specific response:
if (mode == 0x03) { // Mode 3 is standard 80x25 text mode
vga_printf("Display mode set to 80x25 Text Mode (Mode 0x03).\n");
vga_init(); // Re-initialize the text mode
} else {
// Simple I/O example based on the original structure (Caution: Incomplete for full mode set)
outb(VGA_PORT, mode); // Example function to write to a port
vga_printf("Attempting to set display mode to 0x%x. (Warning: May break current display)\n", mode);
}
} }
// Clear the display // Clear the display
void clear_display(void) { void clear_display(void) {
// Use the VGA driver's clear function, typically clearing to black on light grey // Clear the display by filling it with a color
// or black on black. We'll use the black on light grey from vga_init for consistency. // This is a placeholder for actual clearing logic
vga_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY)); // You would typically write to video memory here
// Reset cursor to 0, 0
vga_set_cursor_position(0, 0);
}
// Helper function to write a string
void display_write_string(const char* str) {
// Use the VGA driver's string writing function
vga_write_string(str, strlen(str));
}
// Helper function to print a formatted string
void display_printf(const char* format, ...) {
// Use the VGA driver's printf function
va_list args;
va_start(args, format);
// The vga_printf function already handles the va_list internally,
// so we can just call it directly.
vga_printf(format, args);
va_end(args);
} }

View File

@@ -2,21 +2,13 @@
#define DISPLAY_H #define DISPLAY_H
#include <stdint.h> #include <stdint.h>
#include "vga.h" // Include VGA functions
#define VGA_PORT 0x3C0 // Base port for VGA (Often used for general control, though 0x3D4/0x3D5 are used for cursor) #define VGA_PORT 0x3C0 // Base port for VGA
// Function prototypes // Function prototypes
void init_display(void); void init_display(void);
void enumerate_displays(void); void enumerate_displays(void);
void set_display_mode(uint8_t mode); // In this context, modes are typically BIOS or VESA modes, which are complex. void set_display_mode(uint8_t mode);
// We'll treat this as a placeholder/simple mode call.
void clear_display(void); void clear_display(void);
// New function to write a string using the VGA driver
void display_write_string(const char* str);
// New function to print a formatted string using the VGA driver
void display_printf(const char* format, ...);
#endif // DISPLAY_H #endif // DISPLAY_H

View File

@@ -1,66 +0,0 @@
#include "gui.h"
#include "vga.h" // VGA functions for drawing and clearing screen
#include "framebuffer.h" // For pixel manipulation if needed
// Initialize the GUI (could set up any global state or variables here)
void gui_init(void) {
// Clear the screen with black or any color
gui_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_WHITE));
}
// Draw a window (simple rectangle with a title)
void gui_draw_window(gui_window_t* window) {
// Draw the window's border
for (uint32_t y = 0; y < window->height; ++y) {
for (uint32_t x = 0; x < window->width; ++x) {
// Check if we are at the border
if (x == 0 || y == 0 || x == window->width - 1 || y == window->height - 1) {
vga_put_entry_at('#', vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK), window->x + x, window->y + y);
} else {
// Fill the inside of the window
vga_put_entry_at(' ', vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_BLACK), window->x + x, window->y + y);
}
}
}
// Draw the title at the top
if (window->title) {
size_t i = 0;
while (window->title[i] != '\0' && i < window->width - 2) {
vga_put_entry_at(window->title[i], vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK), window->x + i + 1, window->y);
i++;
}
}
}
// Draw a button (a simple rectangle with text in the middle)
void gui_draw_button(gui_button_t* button) {
for (uint32_t y = 0; y < button->height; ++y) {
for (uint32_t x = 0; x < button->width; ++x) {
// Check if we are at the border
if (x == 0 || y == 0 || x == button->width - 1 || y == button->height - 1) {
vga_put_entry_at('#', vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK), button->x + x, button->y + y);
} else {
// Fill the inside of the button
vga_put_entry_at(' ', vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_BLACK), button->x + x, button->y + y);
}
}
}
// Draw the label in the center of the button
size_t label_len = 0;
while (button->label[label_len] != '\0') {
label_len++;
}
size_t start_x = button->x + (button->width - label_len) / 2;
size_t start_y = button->y + (button->height - 1) / 2;
for (size_t i = 0; i < label_len; ++i) {
vga_put_entry_at(button->label[i], vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK), start_x + i, start_y);
}
}
// Clear the screen with a color
void gui_clear(uint32_t color) {
vga_clear(color); // Just clear the VGA screen with a solid color
}

View File

@@ -1,34 +0,0 @@
#ifndef GUI_H
#define GUI_H
#include <stdint.h>
#include <stddef.h>
#define GUI_WINDOW_WIDTH 80
#define GUI_WINDOW_HEIGHT 25
#define GUI_BUTTON_WIDTH 10
#define GUI_BUTTON_HEIGHT 3
// Window structure
typedef struct {
uint32_t x, y;
uint32_t width, height;
uint32_t color; // Background color
const char* title;
} gui_window_t;
// Button structure
typedef struct {
uint32_t x, y;
uint32_t width, height;
uint32_t color; // Background color
const char* label;
} gui_button_t;
// Function prototypes for GUI elements
void gui_init(void);
void gui_draw_window(gui_window_t* window);
void gui_draw_button(gui_button_t* button);
void gui_clear(uint32_t color);
#endif // GUI_H

View File

@@ -1,65 +0,0 @@
#include "hid.h"
#include "usb.h"
#include "mouse.h"
#include "keyboard.h"
#include "print.h"
#include <stdint.h>
#include <stdbool.h>
// Global variables
static bool hid_initialized = false;
void hid_init(void) {
if (hid_initialized) return;
hid_initialized = true;
// Initialize keyboard and mouse HID handling
keyboard_init();
// Assume USB mouse has been initialized and is connected.
usb_hid_init(); // Initializes USB HID for both keyboard and mouse
}
void hid_process_report(uint8_t* report, uint8_t length) {
// Process the HID report based on its type
if (length == 8) { // Assuming a standard 8-byte report for HID keyboard
keyboard_hid_report_t* k_report = (keyboard_hid_report_t*) report;
hid_process_keyboard_report(k_report);
} else if (length == 3) { // Assuming a standard 3-byte report for HID mouse
mouse_hid_report_t* m_report = (mouse_hid_report_t*) report;
hid_process_mouse_report(m_report);
}
}
// Handle HID keyboard report
void hid_process_keyboard_report(const keyboard_hid_report_t* report) {
// Iterate over the keycodes and process key presses
for (int i = 0; i < 6; i++) {
uint8_t keycode = report->keycodes[i];
if (keycode != 0) {
char key = scancode_map[keycode];
if (key) {
keyboard_buffer_add(key);
}
}
}
}
// Handle HID mouse report
void hid_process_mouse_report(const mouse_hid_report_t* report) {
// Process mouse movement and button clicks
mouse_data.x += report->x;
mouse_data.y += report->y;
mouse_data.left_button = (report->buttons & 0x01) != 0;
mouse_data.right_button = (report->buttons & 0x02) != 0;
print_hex((uint32_t)mouse_data.x, 1, 1);
print_hex((uint32_t)mouse_data.y, 1, 1);
print_hex((uint32_t)report->buttons, 1, 1);
}
// Parse the HID descriptor (for parsing USB HID device descriptors)
bool hid_parse_descriptor(uint8_t* descriptor, uint32_t length) {
// HID descriptors are defined in the USB HID specification, we'll need to parse them here.
// For now, just return true assuming we have a valid descriptor.
return true;
}

View File

@@ -1,46 +0,0 @@
#ifndef HID_H
#define HID_H
#include <stdint.h>
#include <stdbool.h>
// HID Report types
#define HID_REPORT_INPUT 0x01
#define HID_REPORT_OUTPUT 0x02
#define HID_REPORT_FEATURE 0x03
// HID usage page constants (USB HID)
#define HID_USAGE_PAGE_GENERIC 0x01
#define HID_USAGE_KEYBOARD 0x06
#define HID_USAGE_MOUSE 0x02
// HID keyboard and mouse data
typedef struct {
uint8_t modifier; // Modifier keys (shift, ctrl, alt, etc.)
uint8_t reserved; // Reserved byte
uint8_t keycodes[6]; // Keycodes for keys pressed
} keyboard_hid_report_t;
typedef struct {
uint8_t buttons; // Mouse buttons (bitwise: 0x01 = left, 0x02 = right, 0x04 = middle)
int8_t x; // X axis movement
int8_t y; // Y axis movement
int8_t wheel; // Mouse wheel
} mouse_hid_report_t;
// Initialize the HID subsystem
void hid_init(void);
// Process an incoming HID report
void hid_process_report(uint8_t* report, uint8_t length);
// Process HID keyboard report
void hid_process_keyboard_report(const keyboard_hid_report_t* report);
// Process HID mouse report
void hid_process_mouse_report(const mouse_hid_report_t* report);
// USB HID report descriptor parsing
bool hid_parse_descriptor(uint8_t* descriptor, uint32_t length);
#endif // HID_H

View File

@@ -2,91 +2,64 @@
#include "io.h" #include "io.h"
#include "isr.h" #include "isr.h"
#include "terminal.h" #include "terminal.h"
#include <stddef.h>
#define KEYBOARD_DATA_PORT 0x60 #define KEYBOARD_DATA_PORT 0x60
#define KEY_BUFFER_SIZE 256 #define KEY_BUFFER_SIZE 256
// Use volatile so the compiler knows these change inside interrupts static char key_buffer[KEY_BUFFER_SIZE];
static volatile char key_buffer[KEY_BUFFER_SIZE]; static uint8_t buffer_head = 0; // Write position (interrupt)
static volatile uint8_t buffer_head = 0; static uint8_t buffer_tail = 0; // Read position (get_char)
static volatile uint8_t buffer_tail = 0; static uint8_t buffer_count = 0;
static volatile uint8_t buffer_count = 0; static uint8_t buffer_index = 0;
// Exported map: Removed 'static' so hid.c can reference it if needed // Basic US QWERTY keymap (scancode to ASCII)
const char scancode_map[128] = { static const char scancode_map[128] = {
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', // 0x00 - 0x09
'9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', // 0x0A - 0x13
't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 't', 'y', 'z', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x14 - 0x1D
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', // 0x1E - 0x27
'\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', ';', '\'', '`', 0, '\\', 'x', 'c', 'v', 'b', // 0x28 - 0x31
'm', ',', '.', '/', 0, '*', 0, ' ', 0 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, // 0x32 - 0x3B
// rest can be filled as needed
}; };
/** // Interrupt handler for IRQ1
* Shared function used by both PS/2 (callback) and USB (hid.c) void keyboard_callback(void) {
* This fixes the "undefined reference to keyboard_buffer_add" error. uint8_t scancode = inb(KEYBOARD_DATA_PORT);
*/
void keyboard_buffer_add(char c) { if (scancode & 0x80) return; // Ignore key release
char c = scancode_map[scancode];
if (!c) return; if (!c) return;
uint8_t next_head = (buffer_head + 1) % KEY_BUFFER_SIZE; uint8_t next_head = (buffer_head + 1) % KEY_BUFFER_SIZE;
// If buffer is full, we must drop the key // Drop key if buffer full
if (next_head == buffer_tail) { if (next_head == buffer_tail) return;
return;
}
key_buffer[buffer_head] = c; key_buffer[buffer_head] = c;
buffer_head = next_head; buffer_head = next_head;
buffer_count++; buffer_count++;
// Echo to terminal
terminal_putchar(c); terminal_putchar(c);
} }
/** void keyboard_init() {
* Hardware Interrupt Handler for PS/2 register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21)
*/
void keyboard_callback(void) {
uint8_t scancode = inb(KEYBOARD_DATA_PORT);
// Ignore break codes (key release)
if (scancode & 0x80) return;
char c = scancode_map[scancode];
keyboard_buffer_add(c);
} }
void keyboard_init(void) { // Blocking read (returns one char)
buffer_head = 0;
buffer_tail = 0;
buffer_count = 0;
// IRQ1 is usually mapped to IDT entry 33
register_interrupt_handler(33, keyboard_callback);
}
/**
* Blocking read with a safe HLT to prevent CPU 100% usage
*/
char keyboard_get_char(void) { char keyboard_get_char(void) {
char c; while (buffer_count == 0) {
__asm__ __volatile__("hlt"); // Better than busy loop
while (1) {
__asm__ __volatile__("cli"); // Disable interrupts to check buffer_count safely
if (buffer_count > 0) {
c = key_buffer[buffer_tail];
buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE;
buffer_count--;
__asm__ __volatile__("sti"); // Re-enable interrupts after reading
return c;
}
/* * IMPORTANT: 'sti' followed by 'hlt' is guaranteed by x86
* to execute 'hlt' BEFORE the next interrupt can trigger.
* This prevents the race condition hang.
*/
__asm__ __volatile__("sti; hlt");
} }
char c;
__asm__ __volatile__("cli");
c = key_buffer[buffer_tail];
buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE;
buffer_count--;
__asm__ __volatile__("sti");
return c;
} }

View File

@@ -1,12 +1,7 @@
#ifndef KEYBOARD_H #ifndef KEYBOARD_H
#define KEYBOARD_H #define KEYBOARD_H
#include <stdint.h>
void keyboard_init(void); void keyboard_init(void);
void keyboard_buffer_add(char c); char keyboard_get_char(void); // Blocking read from buffer
char keyboard_get_char(void);
extern const char scancode_map[128];
#endif #endif

View File

@@ -1,18 +1,21 @@
#include "memmap.h" #include "memmap.h"
#define BOOTLOADER_MEMMAP_COUNT_ADDR MEMMAP_BASE
#define BOOTLOADER_MEMMAP_ADDR (MEMMAP_BASE + 4)
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) { uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries) {
// Read the number of entries found by the bootloader
uint32_t entries_found = *(uint32_t*)BOOTLOADER_MEMMAP_COUNT_ADDR;
memory_map_entry_t *bios_data = (memory_map_entry_t*)BOOTLOADER_MEMMAP_ADDR;
uint32_t count = 0; uint32_t count = 0;
while (count < entries_found && count < max_entries) {
map[count] = bios_data[count]; if (max_entries >= 1) {
map[count].base_addr = 0x00000000;
map[count].length = 0x0009FC00;
map[count].type = 1;
count++;
}
if (max_entries >= 2) {
map[count].base_addr = 0x00100000;
map[count].length = 0x1FF00000;
map[count].type = 1;
count++; count++;
} }
return count; return count;
} }

View File

@@ -7,7 +7,6 @@ typedef struct {
uint64_t base_addr; uint64_t base_addr;
uint64_t length; uint64_t length;
uint32_t type; uint32_t type;
uint32_t ext;
} __attribute__((packed)) memory_map_entry_t; } __attribute__((packed)) memory_map_entry_t;
uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries); uint32_t get_memory_map(memory_map_entry_t *map, uint32_t max_entries);

View File

@@ -5,7 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
// Mouse buffer // Mouse buffer
mouse_data_t mouse_data; static mouse_data_t mouse_data;
// Read USB mouse data // Read USB mouse data
mouse_data_t usb_read_mouse(void) { mouse_data_t usb_read_mouse(void) {

View File

@@ -12,8 +12,6 @@ typedef struct {
bool right_button; bool right_button;
} mouse_data_t; } mouse_data_t;
extern mouse_data_t mouse_data;
// Function declarations for USB 1.x HID mouse support // Function declarations for USB 1.x HID mouse support
bool usb_mouse_init(void); bool usb_mouse_init(void);
bool usb_mouse_detected(void); bool usb_mouse_detected(void);

View File

@@ -1,109 +0,0 @@
#include "pci.h"
#include "io.h"
/* --- Configuration Access Functions --- */
uint32_t pci_config_read_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t address = (uint32_t)((uint32_t)1 << 31) |
((uint32_t)bus << 16) |
((uint32_t)slot << 11) |
((uint32_t)func << 8) |
(offset & 0xFC);
outl(PCI_CONFIG_ADDRESS, address);
return inl(PCI_CONFIG_DATA);
}
void pci_config_write_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t data) {
uint32_t address = (uint32_t)((uint32_t)1 << 31) |
((uint32_t)bus << 16) |
((uint32_t)slot << 11) |
((uint32_t)func << 8) |
(offset & 0xFC);
outl(PCI_CONFIG_ADDRESS, address);
outl(PCI_CONFIG_DATA, data);
}
/* To read a word or byte, we read the Dword and shift/mask */
uint16_t pci_config_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t dword = pci_config_read_dword(bus, slot, func, offset);
return (uint16_t)((dword >> ((offset & 2) * 8)) & 0xFFFF);
}
uint8_t pci_config_read_byte(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t dword = pci_config_read_dword(bus, slot, func, offset);
return (uint8_t)((dword >> ((offset & 3) * 8)) & 0xFF);
}
/* --- BAR Decoding Logic --- */
pci_bar_t pci_get_bar(uint8_t bus, uint8_t slot, uint8_t func, uint8_t bar_index) {
pci_bar_t bar = {0};
uint8_t offset = PCI_REG_BAR0 + (bar_index * 4);
uint32_t initial_val = pci_config_read_dword(bus, slot, func, offset);
// The Size Masking Trick
pci_config_write_dword(bus, slot, func, offset, 0xFFFFFFFF);
uint32_t mask = pci_config_read_dword(bus, slot, func, offset);
pci_config_write_dword(bus, slot, func, offset, initial_val); // Restore
if (initial_val & 0x1) {
// I/O Space BAR
bar.is_io = true;
bar.base_address = initial_val & 0xFFFFFFFC;
bar.size = ~(mask & 0xFFFFFFFC) + 1;
} else {
// Memory Space BAR
bar.is_io = false;
bar.base_address = initial_val & 0xFFFFFFF0;
bar.is_prefetchable = (initial_val & 0x8) != 0;
bar.size = ~(mask & 0xFFFFFFF0) + 1;
}
return bar;
}
/* --- Enumeration and Discovery --- */
void pci_check_function(uint8_t bus, uint8_t slot, uint8_t func) {
uint16_t vendor_id = pci_config_read_word(bus, slot, func, PCI_REG_VENDOR_ID);
if (vendor_id == 0xFFFF) return;
uint16_t device_id = pci_config_read_word(bus, slot, func, PCI_REG_DEVICE_ID);
uint8_t class_code = pci_config_read_byte(bus, slot, func, PCI_REG_CLASS);
/* Optional: Set Master Latency Timer if it is 0.
A value of 32 (0x20) or 64 (0x40) is typical.
*/
uint8_t latency = pci_config_read_byte(bus, slot, func, PCI_REG_LATENCY_TIMER);
if (latency == 0) {
// pci_config_write_byte would be needed here, or write a dword with the byte modified
uint32_t reg_0c = pci_config_read_dword(bus, slot, func, 0x0C);
reg_0c |= (0x20 << 8); // Set latency to 32
pci_config_write_dword(bus, slot, func, 0x0C, reg_0c);
}
// Replace with your kernel's print/logging function
// printf("Found PCI Device: %x:%x Class: %x at %d:%d:%d\n", vendor_id, device_id, class_code, bus, slot, func);
}
void pci_init(void) {
for (uint16_t bus = 0; bus < 256; bus++) {
for (uint8_t slot = 0; slot < 32; slot++) {
// Check Function 0 first
uint16_t vendor = pci_config_read_word(bus, slot, 0, PCI_REG_VENDOR_ID);
if (vendor == 0xFFFF) continue;
pci_check_function(bus, slot, 0);
// Check if this is a multi-function device
uint8_t header_type = pci_config_read_byte(bus, slot, 0, PCI_REG_HEADER_TYPE);
if (header_type & 0x80) {
// Check functions 1-7
for (uint8_t func = 1; func < 8; func++) {
pci_check_function(bus, slot, func);
}
}
}
}
}

View File

@@ -1,60 +0,0 @@
#ifndef PCI_H
#define PCI_H
#include <stdint.h>
#include <stdbool.h>
/* I/O Ports for PCI Configuration Mechanism #1 */
#define PCI_CONFIG_ADDRESS 0xCF8
#define PCI_CONFIG_DATA 0xCFC
/* Common PCI Configuration Register Offsets */
#define PCI_REG_VENDOR_ID 0x00
#define PCI_REG_DEVICE_ID 0x02
#define PCI_REG_COMMAND 0x04
#define PCI_REG_STATUS 0x06
#define PCI_REG_REVISION_ID 0x08
#define PCI_REG_PROG_IF 0x09
#define PCI_REG_SUBCLASS 0x0A
#define PCI_REG_CLASS 0x0B
#define PCI_REG_CACHE_LINE_SIZE 0x0C
#define PCI_REG_LATENCY_TIMER 0x0D
#define PCI_REG_HEADER_TYPE 0x0E
#define PCI_REG_BIST 0x0F
#define PCI_REG_BAR0 0x10
#define PCI_REG_BAR1 0x14
#define PCI_REG_BAR2 0x18
#define PCI_REG_BAR3 0x1C
#define PCI_REG_BAR4 0x20
#define PCI_REG_BAR5 0x24
#define PCI_REG_INTERRUPT_LINE 0x3C
typedef struct {
uint32_t base_address;
uint32_t size;
bool is_io;
bool is_prefetchable; // Only for Memory BARs
} pci_bar_t;
typedef struct {
uint8_t bus;
uint8_t device;
uint8_t function;
uint16_t vendor_id;
uint16_t device_id;
uint8_t class_code;
uint8_t subclass;
uint8_t interrupt_line;
} pci_dev_t;
/* Function Prototypes */
uint32_t pci_config_read_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
void pci_config_write_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t data);
uint16_t pci_config_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
uint8_t pci_config_read_byte(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);
pci_bar_t pci_get_bar(uint8_t bus, uint8_t slot, uint8_t func, uint8_t bar_index);
void pci_init(void);
#endif

View File

@@ -1,107 +0,0 @@
#include "ps2.h"
#include "io.h"
/* --- Controller Synchronization --- */
// Wait until the controller is ready to receive a byte
static void ps2_wait_write() {
while (inb(PS2_STATUS_REG) & PS2_STATUS_INPUT);
}
// Wait until the controller has a byte for us to read
static void ps2_wait_read() {
while (!(inb(PS2_STATUS_REG) & PS2_STATUS_OUTPUT));
}
/* --- Initialization --- */
void ps2_write_device(uint8_t command) {
ps2_wait_write();
outb(PS2_DATA_PORT, command);
}
void ps2_write_mouse(uint8_t data) {
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_WRITE_MOUSE); // "Next byte goes to mouse"
ps2_wait_write();
outb(PS2_DATA_PORT, data);
}
void ps2_init(void) {
// 1. Disable Devices
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_DISABLE_KB);
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_DISABLE_MS);
// 2. Flush Output Buffer
while (inb(PS2_STATUS_REG) & PS2_STATUS_OUTPUT) {
inb(PS2_DATA_PORT);
}
// 3. Set Controller Configuration Byte
// Bit 0: KB Interrupt, Bit 1: Mouse Interrupt, Bit 6: Translation
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_READ_CONFIG);
ps2_wait_read();
uint8_t status = inb(PS2_DATA_PORT);
status |= (1 << 0) | (1 << 1); // Enable IRQ 1 and IRQ 12
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_WRITE_CONFIG);
ps2_wait_write();
outb(PS2_DATA_PORT, status);
// 4. Enable Devices
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_ENABLE_KB);
ps2_wait_write();
outb(PS2_COMMAND_REG, PS2_CMD_ENABLE_MS);
// 5. Initialize Mouse (The mouse won't send IRQs until you tell it to)
ps2_write_mouse(MOUSE_CMD_SET_DEFAULTS);
ps2_wait_read(); inb(PS2_DATA_PORT); // Read ACK (0xFA)
ps2_write_mouse(MOUSE_CMD_ENABLE_SCAN);
ps2_wait_read(); inb(PS2_DATA_PORT); // Read ACK (0xFA)
}
/* --- IRQ Handlers --- */
// Called from IRQ 1 (Keyboard)
void ps2_keyboard_handler(void) {
uint8_t scancode = inb(PS2_DATA_PORT);
// Process scancode (e.g., put it into a circular buffer)
}
// Called from IRQ 12 (Mouse)
static uint8_t mouse_cycle = 0;
static uint8_t mouse_bytes[3];
void ps2_mouse_handler(void) {
uint8_t status = inb(PS2_STATUS_REG);
// Ensure this is actually mouse data
if (!(status & PS2_STATUS_MOUSE)) return;
mouse_bytes[mouse_cycle++] = inb(PS2_DATA_PORT);
if (mouse_cycle == 3) {
mouse_cycle = 0;
// Byte 0: Flags (Buttons, Signs)
// Byte 1: X Delta
// Byte 2: Y Delta
mouse_state_t state;
state.left_button = (mouse_bytes[0] & 0x01);
state.right_button = (mouse_bytes[0] & 0x02);
state.middle_button = (mouse_bytes[0] & 0x04);
// Handle negative deltas (signed 9-bit logic)
state.x_delta = (int8_t)mouse_bytes[1];
state.y_delta = (int8_t)mouse_bytes[2];
// Update your kernel's internal mouse position here
}
}

View File

@@ -1,45 +0,0 @@
#ifndef PS2_H
#define PS2_H
#include <stdint.h>
#include <stdbool.h>
/* I/O Ports */
#define PS2_DATA_PORT 0x60
#define PS2_STATUS_REG 0x64
#define PS2_COMMAND_REG 0x64
/* Status Register Bits */
#define PS2_STATUS_OUTPUT 0x01 // 1 = Data ready to be read
#define PS2_STATUS_INPUT 0x02 // 1 = Controller busy, don't write yet
#define PS2_STATUS_SYS 0x04 // System flag
#define PS2_STATUS_CMD_DATA 0x08 // 0 = Data written to 0x60, 1 = Cmd to 0x64
#define PS2_STATUS_MOUSE 0x20 // 1 = Mouse data, 0 = Keyboard data
/* Controller Commands */
#define PS2_CMD_READ_CONFIG 0x20
#define PS2_CMD_WRITE_CONFIG 0x60
#define PS2_CMD_DISABLE_MS 0xA7
#define PS2_CMD_ENABLE_MS 0xA8
#define PS2_CMD_DISABLE_KB 0xAD
#define PS2_CMD_ENABLE_KB 0xAE
#define PS2_CMD_WRITE_MOUSE 0xD4
/* Mouse Commands */
#define MOUSE_CMD_SET_DEFAULTS 0xF6
#define MOUSE_CMD_ENABLE_SCAN 0xF4
typedef struct {
int8_t x_delta;
int8_t y_delta;
bool left_button;
bool right_button;
bool middle_button;
} mouse_state_t;
/* Public API */
void ps2_init(void);
void ps2_keyboard_handler(void);
void ps2_mouse_handler(void);
#endif

View File

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

View File

@@ -1,9 +1,9 @@
#include "vga.h"
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include "string_utils.h" #include "string_utils.h"
#include "vga.h"
void outb(uint16_t port, uint8_t value) { void outb(uint16_t port, uint8_t value) {
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
@@ -134,7 +134,7 @@ void vga_printf(const char* format, ...) {
va_end(args); va_end(args);
// Now you can use the buffer with vga_write_string // Now you can use the buffer with vga_write_string
vga_write_string(buffer, strlen(buffer)); // Use my_strlen instead of strlen vga_write_string(buffer, my_strlen(buffer)); // Use my_strlen instead of strlen
} }
void vga_init(void) { void vga_init(void) {

View File

@@ -35,7 +35,6 @@ typedef enum {
// Function prototypes // Function prototypes
uint8_t vga_entry_color(vga_color fg, vga_color bg); uint8_t vga_entry_color(vga_color fg, vga_color bg);
uint16_t vga_entry(unsigned char uc, uint8_t color); uint16_t vga_entry(unsigned char uc, uint8_t color);
void vga_init(void);
void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y); void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y);
void vga_clear(uint8_t color); void vga_clear(uint8_t color);
@@ -51,4 +50,4 @@ void vga_set_cursor_blink_rate(uint8_t rate);
void vga_printf(const char* format, ...); void vga_printf(const char* format, ...);
#endif #endif