mirror of
https://github.com/johndoe6345789/MetalOS.git
synced 2026-04-24 13:45:02 +00:00
Add memory, PCI, and timer modules to kernel
Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
@@ -8,6 +8,9 @@ set(KERNEL_C_SOURCES
|
|||||||
src/main.c
|
src/main.c
|
||||||
src/gdt.c
|
src/gdt.c
|
||||||
src/interrupts.c
|
src/interrupts.c
|
||||||
|
src/memory.c
|
||||||
|
src/pci.c
|
||||||
|
src/timer.c
|
||||||
)
|
)
|
||||||
|
|
||||||
set(KERNEL_ASM_SOURCES
|
set(KERNEL_ASM_SOURCES
|
||||||
|
|||||||
29
kernel/include/kernel/memory.h
Normal file
29
kernel/include/kernel/memory.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef METALOS_KERNEL_MEMORY_H
|
||||||
|
#define METALOS_KERNEL_MEMORY_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "kernel/kernel.h"
|
||||||
|
|
||||||
|
// Memory constants
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
|
||||||
|
// Physical memory manager
|
||||||
|
void pmm_init(BootInfo* boot_info);
|
||||||
|
void* pmm_alloc_page(void);
|
||||||
|
void pmm_free_page(void* page);
|
||||||
|
uint64_t pmm_get_total_memory(void);
|
||||||
|
uint64_t pmm_get_free_memory(void);
|
||||||
|
|
||||||
|
// Simple kernel heap allocator (bump allocator for now)
|
||||||
|
void heap_init(void* start, size_t size);
|
||||||
|
void* kmalloc(size_t size);
|
||||||
|
void* kcalloc(size_t num, size_t size);
|
||||||
|
void kfree(void* ptr);
|
||||||
|
|
||||||
|
// Memory utility functions
|
||||||
|
void* memset(void* dest, int val, size_t count);
|
||||||
|
void* memcpy(void* dest, const void* src, size_t count);
|
||||||
|
int memcmp(const void* s1, const void* s2, size_t count);
|
||||||
|
|
||||||
|
#endif // METALOS_KERNEL_MEMORY_H
|
||||||
31
kernel/include/kernel/pci.h
Normal file
31
kernel/include/kernel/pci.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef METALOS_KERNEL_PCI_H
|
||||||
|
#define METALOS_KERNEL_PCI_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// PCI Configuration Space Registers
|
||||||
|
#define PCI_CONFIG_ADDRESS 0xCF8
|
||||||
|
#define PCI_CONFIG_DATA 0xCFC
|
||||||
|
|
||||||
|
// PCI Device Structure
|
||||||
|
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 prog_if;
|
||||||
|
uint8_t revision_id;
|
||||||
|
uint32_t bar[6]; // Base Address Registers
|
||||||
|
} pci_device_t;
|
||||||
|
|
||||||
|
// PCI Functions
|
||||||
|
void pci_init(void);
|
||||||
|
uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset);
|
||||||
|
void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value);
|
||||||
|
pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id);
|
||||||
|
void pci_enable_bus_mastering(pci_device_t* dev);
|
||||||
|
|
||||||
|
#endif // METALOS_KERNEL_PCI_H
|
||||||
17
kernel/include/kernel/timer.h
Normal file
17
kernel/include/kernel/timer.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef METALOS_KERNEL_TIMER_H
|
||||||
|
#define METALOS_KERNEL_TIMER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Timer frequency (Hz)
|
||||||
|
#define TIMER_FREQUENCY 1000 // 1ms per tick
|
||||||
|
|
||||||
|
// Timer functions
|
||||||
|
void timer_init(uint32_t frequency);
|
||||||
|
uint64_t timer_get_ticks(void);
|
||||||
|
void timer_wait(uint32_t ticks);
|
||||||
|
|
||||||
|
// Timer interrupt handler (called from ISR)
|
||||||
|
void timer_handler(void);
|
||||||
|
|
||||||
|
#endif // METALOS_KERNEL_TIMER_H
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "kernel/interrupts.h"
|
#include "kernel/interrupts.h"
|
||||||
|
#include "kernel/timer.h"
|
||||||
|
|
||||||
// IDT entries (256 interrupts in x86_64)
|
// IDT entries (256 interrupts in x86_64)
|
||||||
static idt_entry_t idt[256];
|
static idt_entry_t idt[256];
|
||||||
@@ -143,10 +144,13 @@ void idt_init(void) {
|
|||||||
|
|
||||||
// Generic interrupt handler
|
// Generic interrupt handler
|
||||||
void interrupt_handler(registers_t* regs) {
|
void interrupt_handler(registers_t* regs) {
|
||||||
// Handle interrupt based on interrupt number
|
// Handle specific interrupts
|
||||||
(void)regs; // Suppress unused warning for now
|
if (regs->int_no == 32) {
|
||||||
|
// Timer interrupt (IRQ0)
|
||||||
|
timer_handler();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Dispatch to specific handlers based on regs->int_no
|
// TODO: Handle other interrupts (keyboard, etc.)
|
||||||
|
|
||||||
// Send EOI (End of Interrupt) to PIC if this was an IRQ
|
// Send EOI (End of Interrupt) to PIC if this was an IRQ
|
||||||
if (regs->int_no >= 32 && regs->int_no < 48) {
|
if (regs->int_no >= 32 && regs->int_no < 48) {
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
#include "kernel/kernel.h"
|
#include "kernel/kernel.h"
|
||||||
#include "kernel/gdt.h"
|
#include "kernel/gdt.h"
|
||||||
#include "kernel/interrupts.h"
|
#include "kernel/interrupts.h"
|
||||||
|
#include "kernel/memory.h"
|
||||||
|
#include "kernel/pci.h"
|
||||||
|
#include "kernel/timer.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Kernel main entry point
|
* Kernel main entry point
|
||||||
@@ -18,15 +21,27 @@
|
|||||||
* Just set up hardware and jump to the QT6 app.
|
* Just set up hardware and jump to the QT6 app.
|
||||||
*/
|
*/
|
||||||
void kernel_main(BootInfo* boot_info) {
|
void kernel_main(BootInfo* boot_info) {
|
||||||
// Suppress unused parameter warning
|
|
||||||
(void)boot_info;
|
|
||||||
|
|
||||||
// Initialize GDT (Global Descriptor Table)
|
// Initialize GDT (Global Descriptor Table)
|
||||||
gdt_init();
|
gdt_init();
|
||||||
|
|
||||||
// Initialize IDT (Interrupt Descriptor Table)
|
// Initialize IDT (Interrupt Descriptor Table)
|
||||||
idt_init();
|
idt_init();
|
||||||
|
|
||||||
|
// Initialize physical memory manager
|
||||||
|
pmm_init(boot_info);
|
||||||
|
|
||||||
|
// Initialize kernel heap (allocate 1MB for kernel heap)
|
||||||
|
void* heap_mem = pmm_alloc_page();
|
||||||
|
if (heap_mem) {
|
||||||
|
heap_init(heap_mem, 256 * PAGE_SIZE); // 1MB heap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize timer (1000 Hz = 1ms per tick)
|
||||||
|
timer_init(TIMER_FREQUENCY);
|
||||||
|
|
||||||
|
// Initialize PCI bus
|
||||||
|
pci_init();
|
||||||
|
|
||||||
// TODO: Set up minimal page tables (identity mapped or simple offset)
|
// TODO: Set up minimal page tables (identity mapped or simple offset)
|
||||||
|
|
||||||
// TODO: Simple memory allocator (bump allocator is fine)
|
// TODO: Simple memory allocator (bump allocator is fine)
|
||||||
|
|||||||
169
kernel/src/memory.c
Normal file
169
kernel/src/memory.c
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* MetalOS Kernel - Memory Management
|
||||||
|
*
|
||||||
|
* Simple physical memory manager and heap allocator
|
||||||
|
* Minimal implementation for single-app OS
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/memory.h"
|
||||||
|
|
||||||
|
// Physical memory bitmap
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#define BITMAP_SIZE 32768 // Supports up to 128MB with 4KB pages
|
||||||
|
|
||||||
|
static uint8_t page_bitmap[BITMAP_SIZE];
|
||||||
|
static uint64_t total_pages = 0;
|
||||||
|
static uint64_t used_pages = 0;
|
||||||
|
|
||||||
|
// Heap for kernel allocations
|
||||||
|
static uint8_t* heap_start = NULL;
|
||||||
|
static uint8_t* heap_current = NULL;
|
||||||
|
static uint8_t* heap_end = NULL;
|
||||||
|
|
||||||
|
// Initialize physical memory manager
|
||||||
|
void pmm_init(BootInfo* boot_info) {
|
||||||
|
(void)boot_info; // TODO: Parse UEFI memory map
|
||||||
|
|
||||||
|
// For now, assume 128MB of usable memory starting at 16MB
|
||||||
|
total_pages = (128 * 1024 * 1024) / PAGE_SIZE;
|
||||||
|
|
||||||
|
// Clear bitmap
|
||||||
|
for (uint64_t i = 0; i < BITMAP_SIZE; i++) {
|
||||||
|
page_bitmap[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
used_pages = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a physical page
|
||||||
|
void* pmm_alloc_page(void) {
|
||||||
|
// Find first free page in bitmap
|
||||||
|
for (uint64_t i = 0; i < total_pages; i++) {
|
||||||
|
uint64_t byte = i / 8;
|
||||||
|
uint64_t bit = i % 8;
|
||||||
|
|
||||||
|
if (!(page_bitmap[byte] & (1 << bit))) {
|
||||||
|
// Mark as used
|
||||||
|
page_bitmap[byte] |= (1 << bit);
|
||||||
|
used_pages++;
|
||||||
|
|
||||||
|
// Return physical address
|
||||||
|
// Assuming memory starts at 16MB
|
||||||
|
return (void*)((0x01000000UL) + (i * PAGE_SIZE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out of memory
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free a physical page
|
||||||
|
void pmm_free_page(void* page) {
|
||||||
|
uint64_t addr = (uint64_t)page;
|
||||||
|
|
||||||
|
// Calculate page index
|
||||||
|
uint64_t page_idx = (addr - 0x01000000UL) / PAGE_SIZE;
|
||||||
|
|
||||||
|
if (page_idx >= total_pages) {
|
||||||
|
return; // Invalid address
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t byte = page_idx / 8;
|
||||||
|
uint64_t bit = page_idx % 8;
|
||||||
|
|
||||||
|
// Mark as free
|
||||||
|
page_bitmap[byte] &= ~(1 << bit);
|
||||||
|
used_pages--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get total memory
|
||||||
|
uint64_t pmm_get_total_memory(void) {
|
||||||
|
return total_pages * PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get free memory
|
||||||
|
uint64_t pmm_get_free_memory(void) {
|
||||||
|
return (total_pages - used_pages) * PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize heap allocator
|
||||||
|
void heap_init(void* start, size_t size) {
|
||||||
|
heap_start = (uint8_t*)start;
|
||||||
|
heap_current = heap_start;
|
||||||
|
heap_end = heap_start + size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple bump allocator (no free support in this version)
|
||||||
|
void* kmalloc(size_t size) {
|
||||||
|
if (!heap_start) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Align to 16 bytes
|
||||||
|
size = (size + 15) & ~15;
|
||||||
|
|
||||||
|
if (heap_current + size > heap_end) {
|
||||||
|
return NULL; // Out of heap memory
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ptr = heap_current;
|
||||||
|
heap_current += size;
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate and zero memory
|
||||||
|
void* kcalloc(size_t num, size_t size) {
|
||||||
|
size_t total = num * size;
|
||||||
|
void* ptr = kmalloc(total);
|
||||||
|
|
||||||
|
if (ptr) {
|
||||||
|
memset(ptr, 0, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory (not implemented in bump allocator)
|
||||||
|
void kfree(void* ptr) {
|
||||||
|
(void)ptr;
|
||||||
|
// TODO: Implement proper free with a real allocator
|
||||||
|
// For now, bump allocator doesn't support freeing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory utility functions
|
||||||
|
|
||||||
|
void* memset(void* dest, int val, size_t count) {
|
||||||
|
uint8_t* d = (uint8_t*)dest;
|
||||||
|
uint8_t v = (uint8_t)val;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
d[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memcpy(void* dest, const void* src, size_t count) {
|
||||||
|
uint8_t* d = (uint8_t*)dest;
|
||||||
|
const uint8_t* s = (const uint8_t*)src;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
d[i] = s[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
int memcmp(const void* s1, const void* s2, size_t count) {
|
||||||
|
const uint8_t* a = (const uint8_t*)s1;
|
||||||
|
const uint8_t* b = (const uint8_t*)s2;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
if (a[i] != b[i]) {
|
||||||
|
return a[i] - b[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
144
kernel/src/pci.c
Normal file
144
kernel/src/pci.c
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* MetalOS Kernel - PCI Bus Support
|
||||||
|
*
|
||||||
|
* Minimal PCI enumeration and configuration
|
||||||
|
* Only what's needed to find and initialize the GPU
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/pci.h"
|
||||||
|
#include "kernel/memory.h"
|
||||||
|
|
||||||
|
// I/O port access functions
|
||||||
|
static inline void outl(uint16_t port, uint32_t value) {
|
||||||
|
__asm__ volatile("outl %0, %1" : : "a"(value), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t inl(uint16_t port) {
|
||||||
|
uint32_t value;
|
||||||
|
__asm__ volatile("inl %1, %0" : "=a"(value) : "Nd"(port));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum devices we'll track
|
||||||
|
#define MAX_PCI_DEVICES 256
|
||||||
|
static pci_device_t pci_devices[MAX_PCI_DEVICES];
|
||||||
|
static uint32_t pci_device_count = 0;
|
||||||
|
|
||||||
|
// Read from PCI configuration space
|
||||||
|
uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
|
||||||
|
uint32_t address = (uint32_t)(
|
||||||
|
((uint32_t)bus << 16) |
|
||||||
|
((uint32_t)device << 11) |
|
||||||
|
((uint32_t)function << 8) |
|
||||||
|
(offset & 0xFC) |
|
||||||
|
0x80000000
|
||||||
|
);
|
||||||
|
|
||||||
|
outl(PCI_CONFIG_ADDRESS, address);
|
||||||
|
return inl(PCI_CONFIG_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to PCI configuration space
|
||||||
|
void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) {
|
||||||
|
uint32_t address = (uint32_t)(
|
||||||
|
((uint32_t)bus << 16) |
|
||||||
|
((uint32_t)device << 11) |
|
||||||
|
((uint32_t)function << 8) |
|
||||||
|
(offset & 0xFC) |
|
||||||
|
0x80000000
|
||||||
|
);
|
||||||
|
|
||||||
|
outl(PCI_CONFIG_ADDRESS, address);
|
||||||
|
outl(PCI_CONFIG_DATA, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probe a PCI device
|
||||||
|
static void pci_probe_device(uint8_t bus, uint8_t device, uint8_t function) {
|
||||||
|
uint32_t vendor_device = pci_read_config(bus, device, function, 0x00);
|
||||||
|
uint16_t vendor_id = vendor_device & 0xFFFF;
|
||||||
|
uint16_t device_id = (vendor_device >> 16) & 0xFFFF;
|
||||||
|
|
||||||
|
// Check if device exists
|
||||||
|
if (vendor_id == 0xFFFF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read class code
|
||||||
|
uint32_t class_rev = pci_read_config(bus, device, function, 0x08);
|
||||||
|
uint8_t class_code = (class_rev >> 24) & 0xFF;
|
||||||
|
uint8_t subclass = (class_rev >> 16) & 0xFF;
|
||||||
|
uint8_t prog_if = (class_rev >> 8) & 0xFF;
|
||||||
|
uint8_t revision_id = class_rev & 0xFF;
|
||||||
|
|
||||||
|
// Store device info
|
||||||
|
if (pci_device_count < MAX_PCI_DEVICES) {
|
||||||
|
pci_device_t* dev = &pci_devices[pci_device_count++];
|
||||||
|
dev->bus = bus;
|
||||||
|
dev->device = device;
|
||||||
|
dev->function = function;
|
||||||
|
dev->vendor_id = vendor_id;
|
||||||
|
dev->device_id = device_id;
|
||||||
|
dev->class_code = class_code;
|
||||||
|
dev->subclass = subclass;
|
||||||
|
dev->prog_if = prog_if;
|
||||||
|
dev->revision_id = revision_id;
|
||||||
|
|
||||||
|
// Read BARs (Base Address Registers)
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
dev->bar[i] = pci_read_config(bus, device, function, 0x10 + (i * 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize PCI subsystem
|
||||||
|
void pci_init(void) {
|
||||||
|
// Scan all buses, devices, and functions
|
||||||
|
for (uint16_t bus = 0; bus < 256; bus++) {
|
||||||
|
for (uint8_t device = 0; device < 32; device++) {
|
||||||
|
// Check if device exists (function 0)
|
||||||
|
uint32_t vendor_device = pci_read_config(bus, device, 0, 0x00);
|
||||||
|
if ((vendor_device & 0xFFFF) == 0xFFFF) {
|
||||||
|
continue; // Device doesn't exist
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_probe_device(bus, device, 0);
|
||||||
|
|
||||||
|
// Check if multi-function device
|
||||||
|
uint32_t header_type = pci_read_config(bus, device, 0, 0x0C);
|
||||||
|
if (header_type & 0x00800000) {
|
||||||
|
// Multi-function device, scan other functions
|
||||||
|
for (uint8_t function = 1; function < 8; function++) {
|
||||||
|
vendor_device = pci_read_config(bus, device, function, 0x00);
|
||||||
|
if ((vendor_device & 0xFFFF) != 0xFFFF) {
|
||||||
|
pci_probe_device(bus, device, function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a PCI device by vendor and device ID
|
||||||
|
pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id) {
|
||||||
|
for (uint32_t i = 0; i < pci_device_count; i++) {
|
||||||
|
if (pci_devices[i].vendor_id == vendor_id &&
|
||||||
|
pci_devices[i].device_id == device_id) {
|
||||||
|
return &pci_devices[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable bus mastering for a device
|
||||||
|
void pci_enable_bus_mastering(pci_device_t* dev) {
|
||||||
|
if (!dev) return;
|
||||||
|
|
||||||
|
// Read command register (offset 0x04)
|
||||||
|
uint32_t command = pci_read_config(dev->bus, dev->device, dev->function, 0x04);
|
||||||
|
|
||||||
|
// Set bus master bit (bit 2)
|
||||||
|
command |= 0x04;
|
||||||
|
|
||||||
|
// Write back
|
||||||
|
pci_write_config(dev->bus, dev->device, dev->function, 0x04, command);
|
||||||
|
}
|
||||||
61
kernel/src/timer.c
Normal file
61
kernel/src/timer.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* MetalOS Kernel - Timer Support
|
||||||
|
*
|
||||||
|
* Simple PIT (Programmable Interval Timer) support
|
||||||
|
* Used for scheduling and timing
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/timer.h"
|
||||||
|
|
||||||
|
// PIT I/O ports
|
||||||
|
#define PIT_CHANNEL0 0x40
|
||||||
|
#define PIT_COMMAND 0x43
|
||||||
|
|
||||||
|
// PIT constants
|
||||||
|
#define PIT_BASE_FREQUENCY 1193182 // Hz
|
||||||
|
|
||||||
|
// Tick counter
|
||||||
|
static volatile uint64_t timer_ticks = 0;
|
||||||
|
|
||||||
|
// I/O port access
|
||||||
|
static inline void outb(uint16_t port, uint8_t value) {
|
||||||
|
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize timer
|
||||||
|
void timer_init(uint32_t frequency) {
|
||||||
|
// Calculate divisor
|
||||||
|
uint32_t divisor = PIT_BASE_FREQUENCY / frequency;
|
||||||
|
|
||||||
|
// Send command byte: channel 0, rate generator, lo/hi byte
|
||||||
|
outb(PIT_COMMAND, 0x36);
|
||||||
|
|
||||||
|
// Send divisor
|
||||||
|
outb(PIT_CHANNEL0, (uint8_t)(divisor & 0xFF));
|
||||||
|
outb(PIT_CHANNEL0, (uint8_t)((divisor >> 8) & 0xFF));
|
||||||
|
|
||||||
|
// Enable timer interrupt (IRQ0)
|
||||||
|
// Unmask IRQ0 in PIC
|
||||||
|
uint8_t mask;
|
||||||
|
__asm__ volatile("inb $0x21, %0" : "=a"(mask));
|
||||||
|
mask &= ~0x01; // Clear bit 0 (IRQ0)
|
||||||
|
outb(0x21, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current tick count
|
||||||
|
uint64_t timer_get_ticks(void) {
|
||||||
|
return timer_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for specified number of ticks
|
||||||
|
void timer_wait(uint32_t ticks) {
|
||||||
|
uint64_t target = timer_ticks + ticks;
|
||||||
|
while (timer_ticks < target) {
|
||||||
|
__asm__ volatile("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer interrupt handler
|
||||||
|
void timer_handler(void) {
|
||||||
|
timer_ticks++;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user