mirror of
https://github.com/gbowne1/ClassicOS.git
synced 2024-11-21 13:56:53 -08:00
fixed and added a lot more stuff like timer and acpi and ahci stub code and a kernel loader as well as worked on pci enumeration
This commit is contained in:
parent
ad70cfd836
commit
528fb00d49
BIN
.vscode/browse.vc.db
vendored
BIN
.vscode/browse.vc.db
vendored
Binary file not shown.
BIN
.vscode/browse.vc.db-shm
vendored
BIN
.vscode/browse.vc.db-shm
vendored
Binary file not shown.
@ -40,16 +40,58 @@ start:
|
|||||||
call printstr
|
call printstr
|
||||||
|
|
||||||
; Load the kernel into memory
|
; Load the kernel into memory
|
||||||
; ...
|
%include "kernel_loader.asm"
|
||||||
|
|
||||||
; Set up GDT, IDT, IVT
|
; Set up IVT (GTD and IDT not used in 16 bit real mode)
|
||||||
; ...
|
; Define interrupt handlers (replace with your actual handler code)
|
||||||
|
div_by_zero_handler:
|
||||||
|
cli
|
||||||
|
hlt
|
||||||
|
|
||||||
|
timer_handler:
|
||||||
|
; Your timer interrupt handler code
|
||||||
|
hlt
|
||||||
|
ret
|
||||||
|
|
||||||
|
; IVT entries (offset 0 for all handlers since segment 0)
|
||||||
|
times 0x100 dw 0x0000 ; Initialize unused entries
|
||||||
|
dw offset div_by_zero_handler ; Interrupt 0 (Division by Zero)
|
||||||
|
times 0x7 dw 0x0000 ; Skip unused entries
|
||||||
|
dw offset timer_handler ; Interrupt 0x8 (Timer)
|
||||||
|
|
||||||
; Switch to protected mode
|
; Switch to protected mode
|
||||||
; ...
|
; Enable Protected Mode
|
||||||
|
switch_to_protected_mode:
|
||||||
|
cli ; Disable interrupts
|
||||||
|
mov eax, cr0 ; Get current CR0 value
|
||||||
|
or eax, 0x01 ; Set PE bit (Protected Mode Enable)
|
||||||
|
mov cr0, eax ; Write modified CR0 value back
|
||||||
|
|
||||||
; Set up stack and start executing kernel's code
|
; Set up stack and start executing kernel's code
|
||||||
; ...
|
; Define GDT structure (replace with your actual GDT definition)
|
||||||
|
gdt_start: ; Beginning of GDT
|
||||||
|
times 5 dd 0 ; Null descriptor entries (optional)
|
||||||
|
gdt_code: ; Code segment descriptor
|
||||||
|
dw 0xffff ; Segment size (limit)
|
||||||
|
dw 0x0000 ; Segment base address (low)
|
||||||
|
db 0x0 ; Segment base address (high)
|
||||||
|
db 10011010b ; Access rights (present, readable, conforming, executable)
|
||||||
|
db 11001111b ; Access rights (long mode, 4-granularity, size)
|
||||||
|
gdt_end: ; End of GDT
|
||||||
|
gdt_descriptor:
|
||||||
|
equ gdt_end - gdt_start ; Size of GDT
|
||||||
|
dw gdt_descriptor ; Offset of GDT
|
||||||
|
dd gdt_start ; Base address of GDT
|
||||||
|
|
||||||
|
; Load the GDT
|
||||||
|
lgdt [gdt_descriptor] ; Load GDT descriptor into GDTR
|
||||||
|
|
||||||
|
; Set up Stack (replace with your actual stack segment and address)
|
||||||
|
mov ss, data_segment ; Set stack segment selector
|
||||||
|
mov esp, 0x100000 ; Set stack pointer (top of stack)
|
||||||
|
|
||||||
|
; Start Kernel Execution
|
||||||
|
jmp farptr kernel_entry ; Jump to kernel entry point (replace with actual address)
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
; printstr routine, prints the string pointed by si using int 0x10 ;
|
; printstr routine, prints the string pointed by si using int 0x10 ;
|
||||||
|
28
src/boot/kernel_loader.asm
Normal file
28
src/boot/kernel_loader.asm
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
; Define constants (adjust as needed)
|
||||||
|
boot_drive equ 0x00 ; Drive to load kernel from (usually 0 for primary)
|
||||||
|
kernel_sector equ 1 ; Sector containing the kernel (adjust for your kernel's location)
|
||||||
|
kernel_segments equ 4 ; Number of sectors to load (adjust for your kernel size)
|
||||||
|
kernel_load_address equ 0x1000 ; Memory address to load the kernel
|
||||||
|
|
||||||
|
; ... (Function prototypes and int_13h implementation copied from previous response)
|
||||||
|
; Function prototypes for readability
|
||||||
|
; (These functions are not strictly necessary in NASM, but improve code organization)
|
||||||
|
; Prototype for BIOS disk read interrupt (INT 13h)
|
||||||
|
void int_13h(unsigned int ah, unsigned int al, unsigned int dx, unsigned int ch, unsigned int cl, unsigned int bx);
|
||||||
|
|
||||||
|
; Function prototype for error handling or printing a message
|
||||||
|
void error_handler(const char *message);
|
||||||
|
; Main kernel loading code
|
||||||
|
mov bx, kernel_load_address ; Set load address
|
||||||
|
|
||||||
|
; Loop to load kernel sectors
|
||||||
|
mov cx, 0 ; Initialize counter
|
||||||
|
loop_load:
|
||||||
|
int_13h(0x02, kernel_segments, boot_drive * 256 + kernel_sector, ch, cl, bx) ; Read sectors
|
||||||
|
add bx, 512 * kernel_segments ; Update load address
|
||||||
|
inc cx ; Increment counter
|
||||||
|
cmp cx, kernel_segments ; Check if all sectors loaded
|
||||||
|
jne loop_load ; Jump back if not finished
|
||||||
|
|
||||||
|
; Success - kernel is now loaded into memory
|
||||||
|
ret ; Return to the main bootloader code
|
@ -1,68 +1,169 @@
|
|||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// PCI bus controller base address
|
// PCI bus controller base address
|
||||||
#define PCI_BASE_ADDRESS 0x0000
|
#define PCI_BASE_ADDRESS 0x0000
|
||||||
|
|
||||||
// PCI bus controller data port
|
// PCI bus controller data port
|
||||||
#define PCI_DATA_PORT 0x00
|
#define PCI_DATA_PORT 0x00
|
||||||
|
|
||||||
// PCI bus controller command port
|
// PCI bus controller command port
|
||||||
#define PCI_COMMAND_PORT 0x01
|
#define PCI_COMMAND_PORT 0x01
|
||||||
|
|
||||||
// Initialize the PCI bus
|
// Initialize the PCI bus
|
||||||
void pci_init()
|
void pci_init()
|
||||||
{
|
{
|
||||||
// Enable PCI bus master
|
// Enable PCI bus master
|
||||||
pci_write(PCI_COMMAND_PORT, 0x04);
|
pci_write(PCI_COMMAND_PORT, 0x04);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect and configure PCI devices
|
// Read from a PCI device
|
||||||
void pci_detect_devices()
|
uint8_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset)
|
||||||
{
|
{
|
||||||
// Scan all PCI buses and devices
|
uint16_t port = PCI_BASE_ADDRESS | ((uint16_t)bus << 8) | ((uint16_t)device << 3) | ((uint16_t)function << 0x0B) | ((uint16_t)offset & 0xFC);
|
||||||
for (int bus = 0; bus < 256; bus++) {
|
uint8_t value;
|
||||||
for (int device = 0; device < 32; device++) {
|
|
||||||
for (int function = 0; function < 8; function++) {
|
// Read from the specified port
|
||||||
uint16_t vendor_id = pci_read(bus, device, function, 0x00);
|
__asm__ volatile("inb %1, %0" : "=a"(value) : "dN"(port));
|
||||||
if (vendor_id != 0xFFFF) {
|
|
||||||
uint16_t device_id = pci_read(bus, device, function, 0x02);
|
return value;
|
||||||
uint8_t class_code = pci_read(bus, device, function, 0x0B);
|
}
|
||||||
uint8_t subclass_code = pci_read(bus, device, function, 0x0A);
|
// Function to write a value to a PCI configuration register
|
||||||
uint8_t prog_if = pci_read(bus, device, function, 0x09);
|
void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint8_t value)
|
||||||
uint8_t header_type = pci_read(bus, device, function, 0x0E);
|
{
|
||||||
uint8_t irq_line = pci_read(bus, device, function, 0x3C);
|
// ... (your existing implementation for setting up the port address)
|
||||||
uint32_t bar0 = pci_read(bus, device, function, 0x10);
|
|
||||||
uint32_t bar1 = pci_read(bus, device, function, 0x14);
|
// Write the specified value to the specified offset
|
||||||
uint32_t bar2 = pci_read(bus, device, function, 0x18);
|
__asm__ volatile("outb %0, %1" : : "a"(value), "dN"(port));
|
||||||
uint32_t bar3 = pci_read(bus, device, function, 0x1C);
|
}
|
||||||
uint32_t bar4 = pci_read(bus, device, function, 0x20);
|
|
||||||
uint32_t bar5 = pci_read(bus, device, function, 0x24);
|
// Set the PCI Latency Timer for a device
|
||||||
// Add any necessary device detection and configuration code here
|
void pci_set_latency(uint8_t bus, uint8_t device, uint8_t function, uint8_t value)
|
||||||
}
|
{
|
||||||
}
|
pci_write_config(bus, device, function, 0x0D, value);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
uint16_t pci_read_config_word(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset)
|
||||||
|
{
|
||||||
// Read from a PCI device
|
uint16_t value = 0;
|
||||||
uint8_t pci_read(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset)
|
value |= pci_read_config(bus, device, function, offset);
|
||||||
{
|
value |= (uint16_t)pci_read_config(bus, device, function, offset + 1) << 8;
|
||||||
uint16_t port = PCI_BASE_ADDRESS | ((uint16_t)bus << 8) | ((uint16_t)device << 3) | ((uint16_t)function << 0x0B) | ((uint16_t)offset & 0xFC);
|
return value;
|
||||||
uint8_t value;
|
}
|
||||||
|
|
||||||
// Read from the specified port
|
// Function to read a 32-bit value from PCI configuration space
|
||||||
__asm__ volatile("inb %1, %0" : "=a"(value) : "dN"(port));
|
uint32_t pci_read_config_dword(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset)
|
||||||
|
{
|
||||||
return value;
|
uint32_t value = 0;
|
||||||
}
|
value |= pci_read_config(bus, device, function, offset);
|
||||||
|
value |= (uint32_t)pci_read_config(bus, device, function, offset + 1) << 8;
|
||||||
// Write to a PCI device
|
value |= (uint32_t)pci_read_config(bus, device, function, offset + 2) << 16;
|
||||||
void pci_write(uint16_t port, uint8_t value)
|
value |= (uint32_t)pci_read_config(bus, device, function, offset + 3) << 24;
|
||||||
{
|
return value;
|
||||||
// Write the specified value to the specified port
|
}
|
||||||
__asm__ volatile("outb %0, %1" : : "a"(value), "dN"(port));
|
|
||||||
}
|
// Structure to hold information about a BAR
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t type; // Memory (0) or I/O (1)
|
||||||
|
uint32_t base; // Base address
|
||||||
|
uint32_t size; // Size of the region (in bytes)
|
||||||
|
} bar_t;
|
||||||
|
|
||||||
|
// Function to decode a BAR register
|
||||||
|
bar_t pci_decode_bar(uint32_t bar_value)
|
||||||
|
{
|
||||||
|
bar_t bar;
|
||||||
|
bar.type = (bar_value & 1) ? 1 : 0; // Check bit 0 for type
|
||||||
|
|
||||||
|
// Extract address and size information (logic might need adjustment)
|
||||||
|
bar.base = bar_value & ~((1 << 0) | (1 << 1)); // Mask out type and prefetchable bits
|
||||||
|
|
||||||
|
// Size calculation needs further processing based on Memory Space Enable bit (bit 3)
|
||||||
|
uint32_t size_bits = bar_value & ~(bar.base | (1 << 0) | (1 << 1) | (1 << 3));
|
||||||
|
if (size_bits == 0)
|
||||||
|
{ // Handle case where size is 0
|
||||||
|
bar.size = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Calculate size based on the number of leading 1s in size_bits
|
||||||
|
int num_leading_ones = 0;
|
||||||
|
while (size_bits && (size_bits & 0x80000000))
|
||||||
|
{
|
||||||
|
num_leading_ones++;
|
||||||
|
size_bits <<= 1;
|
||||||
|
}
|
||||||
|
bar.size = 1 << num_leading_ones;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bar;
|
||||||
|
}
|
||||||
|
// Detect and configure PCI devices
|
||||||
|
void pci_detect_devices()
|
||||||
|
{
|
||||||
|
// Scan all PCI buses and devices
|
||||||
|
for (int bus = 0; bus < 256; bus++)
|
||||||
|
{
|
||||||
|
for (int device = 0; device < 32; device++)
|
||||||
|
{
|
||||||
|
for (int function = 0; function < 8; function++)
|
||||||
|
{
|
||||||
|
uint16_t vendor_id = pci_read(bus, device, function, 0x00);
|
||||||
|
if (vendor_id != 0xFFFF)
|
||||||
|
{
|
||||||
|
uint16_t device_id = pci_read(bus, device, function, 0x02);
|
||||||
|
|
||||||
|
// Lookup vendor and device ID in a database (e.g., PCI.ids)
|
||||||
|
const char *device_name = lookup_device_name(vendor_id, device_id);
|
||||||
|
|
||||||
|
if (device_name != NULL)
|
||||||
|
{
|
||||||
|
// Device identified!
|
||||||
|
printk("Found PCI device: %s (vendor: %04x, device: %04x)\n",
|
||||||
|
device_name, vendor_id, device_id);
|
||||||
|
|
||||||
|
// ... (Optional) Further configuration based on device type
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Device not found in database, handle unknown device
|
||||||
|
printk("Found unknown PCI device (vendor: %04x, device: %04x)\n",
|
||||||
|
vendor_id, device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue reading other registers, handling BARs, etc.
|
||||||
|
uint8_t class_code = pci_read(bus, device, function, 0x0B);
|
||||||
|
uint8_t subclass_code = pci_read(bus, device, function, 0x0A);
|
||||||
|
uint8_t prog_if = pci_read(bus, device, function, 0x09);
|
||||||
|
uint8_t header_type = pci_read(bus, device, function, 0x0E);
|
||||||
|
uint8_t irq_line = pci_read(bus, device, function, 0x3C);
|
||||||
|
|
||||||
|
// ... (rest of your code for handling BARs, etc.)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read from a PCI device
|
||||||
|
uint8_t pci_read(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset)
|
||||||
|
{
|
||||||
|
uint16_t port = PCI_BASE_ADDRESS | ((uint16_t)bus << 8) | ((uint16_t)device << 3) | ((uint16_t)function << 0x0B) | ((uint16_t)offset & 0xFC);
|
||||||
|
uint8_t value;
|
||||||
|
|
||||||
|
// Read from the specified port
|
||||||
|
__asm__ volatile("inb %1, %0" : "=a"(value) : "dN"(port));
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to a PCI device
|
||||||
|
void pci_write(uint16_t port, uint8_t value)
|
||||||
|
{
|
||||||
|
// Write the specified value to the specified port
|
||||||
|
__asm__ volatile("outb %0, %1" : : "a"(value), "dN"(port));
|
||||||
|
}
|
||||||
|
@ -7,30 +7,31 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
uint8_t *map_memory(uint32_t base_address, uint32_t size);
|
uint8_t *map_memory(uint32_t base_address, uint32_t size);
|
||||||
|
|
||||||
#define VGA_MEMORY_ADDRESS 0xA0000
|
|
||||||
#define VGA_MEMORY_SIZE 0x60000
|
|
||||||
#define VGA_MEMORY_BASE_ADDRESS 0xA0000
|
|
||||||
|
|
||||||
#define COLOR_GREEN 0x00FF00
|
|
||||||
|
|
||||||
uint8_t *vga_memory = (uint8_t *)VGA_MEMORY_ADDRESS;
|
uint8_t *vga_memory = (uint8_t *)VGA_MEMORY_ADDRESS;
|
||||||
static uint16_t vga_width = VGA_WIDTH;
|
static uint16_t vga_width = VGA_WIDTH;
|
||||||
static uint16_t vga_height = VGA_HEIGHT;
|
static uint16_t vga_height = VGA_HEIGHT;
|
||||||
static uint8_t vga_depth = VGA_DEPTH;
|
static uint8_t vga_depth = VGA_DEPTH;
|
||||||
uint8_t *framebuffer = (uint8_t *)vga_memory;
|
uint8_t *framebuffer = (uint8_t *)vga_memory;
|
||||||
|
uint8_t *map_memory(uint32_t base_address, uint32_t size);
|
||||||
|
uint8_t read_vga_register(uint16_t register_address);
|
||||||
|
int inb(uint16_t port);
|
||||||
|
void outb(uint16_t port, uint8_t data);
|
||||||
|
uint16_t width;
|
||||||
|
uint32_t get_vga_memory_address();
|
||||||
|
|
||||||
// Set a specific pixel color (assuming RGB format)
|
// Set a specific pixel color (assuming RGB format)
|
||||||
int x = 10;
|
int x = 10;
|
||||||
int y = 20;
|
int y = 20;
|
||||||
int color = 0x00FF00; // Green
|
int color = 0x00FF00; // Green
|
||||||
|
|
||||||
framebuffer[y * width + x * 3] = (color & 0x00FF) >> 8; // Green value
|
void some_function()
|
||||||
framebuffer[y * width + x * 3 + 1] = color & 0x00FF; // Blue value
|
{
|
||||||
framebuffer[y * width + x * 3 + 2] = color >> 8; // Red value
|
// ... other code ...
|
||||||
|
uint8_t misc_output_value = read_vga_register(0x3C2); // Initialize here
|
||||||
|
// ... use misc_output_value ...
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t misc_output_value = read_vga_register(0x3C2);
|
uint8_t misc_output_value = read_vga_register(0x3C2);
|
||||||
|
|
||||||
// Check bit 1 to determine text mode (0) or graphics mode (1)
|
// Check bit 1 to determine text mode (0) or graphics mode (1)
|
||||||
if (misc_output_value & 0x02)
|
if (misc_output_value & 0x02)
|
||||||
{
|
{
|
||||||
@ -53,7 +54,7 @@ uint32_t get_vga_memory_address()
|
|||||||
void vga_init(void)
|
void vga_init(void)
|
||||||
{
|
{
|
||||||
// Initialize VGA driver here
|
// Initialize VGA driver here
|
||||||
vga_memory = (uint8_t *)map_memory(VGA_MEMORY_BASE_ADDRESS, VGA_MEMORY_SIZE);
|
vga_memory = (uint8_t *)map_memory(VGA_MEMORY_ADDRESS, VGA_MEMORY_SIZE);
|
||||||
|
|
||||||
if (vga_memory == NULL)
|
if (vga_memory == NULL)
|
||||||
{
|
{
|
||||||
@ -61,38 +62,43 @@ void vga_init(void)
|
|||||||
fprintf(stderr, "Failed to map VGA Memory\n");
|
fprintf(stderr, "Failed to map VGA Memory\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
framebuffer = vga_memory;
|
framebuffer = (uint8_t *)vga_memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace with actual functions to read VGA registers based on your system
|
// Replace with actual functions to read VGA registers based on your system
|
||||||
uint8_t read_vga_register(uint16_t register_address)
|
uint8_t read_vga_register(uint16_t register_address)
|
||||||
{
|
{
|
||||||
uint8_t value = inb(register_address); // Read from the specified port
|
uint8_t value = inb(register_address); // Read from the specified port
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
int inb(uint16_t port) {
|
int inb(uint16_t port)
|
||||||
// Implement port access logic (replace with system-specific function)
|
{
|
||||||
|
uint8_t value;
|
||||||
|
__asm volatile("inb %1, %0" : "=a"(value) : "d"(port));
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void outb(uint16_t port, uint8_t data) {
|
void outb(uint16_t port, uint8_t data)
|
||||||
|
{
|
||||||
|
__asm volatile("outb %0, %1" : : "a"(data), "d"(port));
|
||||||
}
|
}
|
||||||
|
|
||||||
void vga_set_resolution(uint16_t width, uint16_t height, uint8_t depth)
|
void vga_set_resolution(uint16_t width, uint16_t height, uint8_t depth)
|
||||||
{
|
{
|
||||||
// Set VGA resolution here
|
|
||||||
// Example (replace with actual register values and writes based on your system):
|
|
||||||
|
|
||||||
// Sequencer unlock
|
// Sequencer unlock
|
||||||
outb(0x3C4, 0x01);
|
outb(0x3C4, 0x01);
|
||||||
outb(0x3C5, 0x01);
|
outb(0x3C5, 0x01);
|
||||||
|
|
||||||
// Set specific CRTC registers for desired resolution and refresh rate
|
// Set specific CRTC registers for desired resolution and refresh rate
|
||||||
// ... (write to specific VGA registers using outb)
|
outb(0x3D4, 0x01); // Horizontal total
|
||||||
|
outb(0x3D5, 0x5F); // ...
|
||||||
|
// ... (set other CRTC registers)
|
||||||
|
|
||||||
// Graphics controller registers for memory access
|
// Graphics controller registers for memory access
|
||||||
// ... (write to specific VGA registers using outb)
|
outb(0x3CE, 0x05); // Graphics mode
|
||||||
|
outb(0x3CF, 0x03);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vga_draw_pixel(uint16_t x, uint16_t y, uint8_t color)
|
void vga_draw_pixel(uint16_t x, uint16_t y, uint8_t color)
|
||||||
@ -100,6 +106,9 @@ void vga_draw_pixel(uint16_t x, uint16_t y, uint8_t color)
|
|||||||
// Draw a pixel on the screen at (x, y) with the given color
|
// Draw a pixel on the screen at (x, y) with the given color
|
||||||
uint32_t offset = y * vga_width + x;
|
uint32_t offset = y * vga_width + x;
|
||||||
vga_memory[offset] = color;
|
vga_memory[offset] = color;
|
||||||
|
framebuffer[y * width + x * 3] = (color & 0x00FF) >> 8; // Green value
|
||||||
|
framebuffer[y * width + x * 3 + 1] = color & 0x00FF; // Blue value
|
||||||
|
framebuffer[y * width + x * 3 + 2] = color >> 8; // Red value
|
||||||
}
|
}
|
||||||
|
|
||||||
void vga_clear_screen(uint8_t color)
|
void vga_clear_screen(uint8_t color)
|
||||||
|
@ -3,14 +3,22 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef struct {
|
#define VGA_WIDTH 320 // Replace with the desired width
|
||||||
uint16_t width;
|
#define VGA_HEIGHT 200 // Replace with the desired height
|
||||||
uint16_t height;
|
#define VGA_DEPTH 8 // Replace with the desired color depth (e.g., 8 for 256 colors)
|
||||||
uint8_t depth;
|
#define VGA_MEMORY_ADDRESS 0xA0000 // Move to header file
|
||||||
// Add other relevant settings like color palette, etc.
|
#define VGA_MEMORY_SIZE 0x60000 // Move to header file
|
||||||
|
#define COLOR_GREEN 0x00FF00
|
||||||
|
#define VGA_MAP_MEM_ERROR -1
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
uint8_t depth;
|
||||||
|
// Add other relevant settings like color palette, etc.
|
||||||
} vga_display_settings_t;
|
} vga_display_settings_t;
|
||||||
|
|
||||||
void vga_init();
|
|
||||||
void vga_set_resolution(uint16_t width, uint16_t height, uint8_t depth);
|
void vga_set_resolution(uint16_t width, uint16_t height, uint8_t depth);
|
||||||
void vga_draw_pixel(uint16_t x, uint16_t y, uint8_t color);
|
void vga_draw_pixel(uint16_t x, uint16_t y, uint8_t color);
|
||||||
void vga_clear_screen(uint8_t color);
|
void vga_clear_screen(uint8_t color);
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
#include "acpi.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// Function prototypes (implementations below)
|
||||||
|
acpi_fadt_t *acpi_find_fadt();
|
||||||
|
int acpi_validate_rsdt(acpi_fadt_t *fadt);
|
||||||
|
|
||||||
|
// ... (Add other ACPI functions for table parsing, resource access, etc.)
|
||||||
|
|
||||||
|
// Function to locate the FADT table
|
||||||
|
acpi_fadt_t *acpi_find_fadt() {
|
||||||
|
// Read the RSDP (Root System Description Pointer) signature and checksum
|
||||||
|
// at the ACPI base address.
|
||||||
|
acpi_fadt_t *rsdp = (acpi_fadt_t *)ACPI_BASE;
|
||||||
|
if (rsdp->signature != *(uint32_t *)"RSDT" && rsdp->signature != *(uint32_t *)"XSDT") {
|
||||||
|
return NULL; // Not a valid ACPI signature
|
||||||
|
}
|
||||||
|
if (rsdp->checksum != 0) {
|
||||||
|
return NULL; // Invalid checksum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check ACPI revision and use appropriate table address (RSDT or XSDT)
|
||||||
|
return (acpi_fadt_t *)(rsdp->revision >= 2 ? rsdp->xsdt_addr : rsdp->rsdp_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to validate the RSDT (or XSDT) table
|
||||||
|
int acpi_validate_rsdt(acpi_fadt_t *fadt) {
|
||||||
|
// Check table length and perform basic integrity checks
|
||||||
|
// ...
|
||||||
|
|
||||||
|
return 0; // Assuming success, replace with actual validation logic
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef ACPI_H
|
||||||
|
#define ACPI_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// ACPI base address (replace with actual value)
|
||||||
|
#define ACPI_BASE 0xE0000000
|
||||||
|
|
||||||
|
// ACPI Fixed ACPI Description Table (FADT) structure
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t signature; // Signature ("RSDT" or "XSDT")
|
||||||
|
uint32_t length; // Length of the table
|
||||||
|
uint8_t revision; // ACPI revision
|
||||||
|
uint8_t checksum; // Checksum (should be zero)
|
||||||
|
uint32_t oem_id[6]; // OEM ID string
|
||||||
|
uint8_t oem_revision[8]; // OEM revision string
|
||||||
|
uint32_t rsdp_addr; // Physical address of RSDT (for ACPI 1.0)
|
||||||
|
uint32_t xsdt_addr; // Physical address of XSDT (for ACPI 2.0+)
|
||||||
|
// ... other FADT fields (refer to ACPI specification)
|
||||||
|
} __attribute__((packed)) acpi_fadt_t;
|
||||||
|
|
||||||
|
// ... (Add other ACPI table structure definitions as needed)
|
||||||
|
|
||||||
|
#endif /* ACPI_H */
|
36
src/kernel/ahci.c
Normal file
36
src/kernel/ahci.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include "ahci.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Function prototypes (implementations below)
|
||||||
|
uint32_t ahci_read_reg(uint32_t offset);
|
||||||
|
void ahci_write_reg(uint32_t offset, uint32_t value);
|
||||||
|
|
||||||
|
// Function to send an AHCI command
|
||||||
|
int ahci_send_command(uint32_t cmd_slot, ahci_cmd_t *cmd);
|
||||||
|
|
||||||
|
// ... (Add other AHCI functions for initialization, status checks, etc.)
|
||||||
|
|
||||||
|
// Helper function to read a 32-bit register
|
||||||
|
uint32_t ahci_read_reg(uint32_t offset)
|
||||||
|
{
|
||||||
|
return *(volatile uint32_t *)(HBA_PORT_BASE + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to write a 32-bit register
|
||||||
|
void ahci_write_reg(uint32_t offset, uint32_t value)
|
||||||
|
{
|
||||||
|
*(volatile uint32_t *)(HBA_PORT_BASE + offset) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to send an AHCI command
|
||||||
|
int ahci_send_command(uint32_t cmd_slot, ahci_cmd_t *cmd)
|
||||||
|
{
|
||||||
|
// Implement logic to write the command list entry to appropriate registers
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Wait for command completion (check relevant AHCI registers)
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Return status based on completion and error checking
|
||||||
|
// ...
|
||||||
|
}
|
28
src/kernel/ahci.h
Normal file
28
src/kernel/ahci.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef AHCI_H
|
||||||
|
#define AHCI_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Base address of AHCI registers (replace with actual value)
|
||||||
|
#define HBA_PORT_BASE 0x10000000
|
||||||
|
|
||||||
|
// AHCI register offsets
|
||||||
|
#define HBA_CAP 0x00 // Host Capabilities
|
||||||
|
#define HBA_PI 0x0C // Port Interrupt
|
||||||
|
#define HBA_ISR 0x20 // Interrupt Status
|
||||||
|
#define HBA_IMR 0x30 // Interrupt Mask
|
||||||
|
#define HBA_CMD 0x34 // Command List Head
|
||||||
|
#define HBA_CMD_SLOT_0 0 // First command slot
|
||||||
|
|
||||||
|
// AHCI command list structure
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t cmd_sts; // FIS (Frame in flight)
|
||||||
|
uint32_t prdt_addr; // Physical address of PRDT (SATA FIS)
|
||||||
|
uint32_t prdt_length; // Length of PRDT in bytes
|
||||||
|
uint32_t reserved[4]; // Reserved (set to 0)
|
||||||
|
} __attribute__((packed)) ahci_cmd_t;
|
||||||
|
|
||||||
|
// ... (Add other AHCI register definitions and structures as needed)
|
||||||
|
|
||||||
|
#endif /* AHCI_H */
|
@ -1,7 +1,12 @@
|
|||||||
global LoadIDT
|
global LoadIDT ; Declare function as global
|
||||||
|
|
||||||
|
_offset idt db 0
|
||||||
|
|
||||||
|
section .text ; Code section
|
||||||
|
|
||||||
|
%include "idt.h"
|
||||||
|
|
||||||
section .text
|
|
||||||
LoadIDT:
|
LoadIDT:
|
||||||
mov eax, [offset idt] ; Get the IDT address
|
mov eax, [esp + 4] ; Get IDT address (ensure offset idt is defined)
|
||||||
LIDT [eax + 8*0x33] ; Load only the timer interrupt entry (offset 8*0x33)
|
LIDT [eax] ; Load timer interrupt entry (check for correct brackets)
|
||||||
RET
|
RET ; Return from function
|
@ -1,18 +1,19 @@
|
|||||||
#include "./types.h"
|
#include "./types.h"
|
||||||
|
#include <stdint.h>
|
||||||
void gdt_set_entry(gdt_entry_t *entry, uint32_t base, uint32_t limit,
|
|
||||||
uint16_t flags)
|
void gdt_set_entry(gdt_entry_t *entry, uint32_t base, uint32_t limit,
|
||||||
{
|
uint16_t flags)
|
||||||
entry->base = base;
|
{
|
||||||
entry->limit = limit;
|
entry->base = base;
|
||||||
entry->flags = flags;
|
entry->limit = limit;
|
||||||
}
|
entry->flags = flags;
|
||||||
|
}
|
||||||
void idt_set_entry(idt_entry_t *entry, uint32_t base, uint16_t selector,
|
|
||||||
uint16_t flags)
|
void idt_set_entry(idt_entry_t *entry, uint32_t base, uint16_t selector,
|
||||||
{
|
uint16_t flags)
|
||||||
entry->base_low = base & 0xFFFF;
|
{
|
||||||
entry->base_high = (base >> 16) & 0xFFFF;
|
entry->base_low = base & 0xFFFF;
|
||||||
entry->selector = selector;
|
entry->base_high = (base >> 16) & 0xFFFF;
|
||||||
entry->flags = flags;
|
entry->selector = selector;
|
||||||
}
|
entry->flags = flags;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
section .text
|
%include "isr.h"
|
||||||
global isr_handler
|
|
||||||
global _isr_stub
|
|
||||||
|
|
||||||
|
section .data
|
||||||
|
ISR_HAS_ERROR_CODE equ 1
|
||||||
|
ISR_IS_HARDWARE_INTERRUPT equ 1
|
||||||
|
|
||||||
|
section .text
|
||||||
|
global isr_stub
|
||||||
|
|
||||||
|
global _isr_stub
|
||||||
_isr_stub:
|
_isr_stub:
|
||||||
; Save context
|
; Save context
|
||||||
PUSHAD ; Preserve all general-purpose registers
|
PUSHAD ; Preserve all general-purpose registers
|
||||||
@ -10,41 +16,36 @@ _isr_stub:
|
|||||||
CLI
|
CLI
|
||||||
|
|
||||||
; Align stack to 16-byte boundary for performance (if needed)
|
; Align stack to 16-byte boundary for performance (if needed)
|
||||||
; AND ESP, 0xFFFFFFF0
|
AND ESP, 0xFFFFFFF0
|
||||||
|
|
||||||
; Check if an error code is present (typically in EAX)
|
; Check if an error code is present (typically in EAX)
|
||||||
; This is just a placeholder, actual implementation depends on your IDT setup
|
; This is just a placeholder, actual implementation depends on your IDT setup
|
||||||
CMP BYTE [ISR_HAS_ERROR_CODE], 1
|
CMP BYTE [ISR_HAS_ERROR_CODE], 1
|
||||||
JNE no_error_code
|
JNE no_error_code
|
||||||
PUSH DWORD [ESP + 32] ; Push error code
|
PUSH DWORD [ESP + 32] ; Push error code
|
||||||
JMP done_pushing_error_code
|
|
||||||
no_error_code:
|
no_error_code:
|
||||||
PUSH DWORD 0 ; Push a dummy error code
|
PUSH DWORD 0
|
||||||
done_pushing_error_code:
|
MOV EAX, [ESP + 36]
|
||||||
|
PUSH EAX
|
||||||
; Push interrupt number onto the stack
|
; Call the C ISR handler
|
||||||
MOV EAX, [ESP + 36] ; Move interrupt number to EAX (accounting for the dummy/error code)
|
|
||||||
PUSH EAX ; Interrupt number
|
|
||||||
|
|
||||||
; Call isr_handler
|
|
||||||
CALL isr_handler
|
CALL isr_handler
|
||||||
|
|
||||||
; Send EOI to PIC only if it's a hardware interrupt
|
MOV AL, BYTE [ISR_IS_HARDWARE_INTERRUPT]
|
||||||
; This is just a placeholder, actual implementation depends on your IDT setup
|
CMP AL, 1
|
||||||
CMP BYTE [ISR_IS_HARDWARE_INTERRUPT], 1
|
|
||||||
JNE skip_eoi
|
JNE skip_eoi
|
||||||
MOV AL, 0x20
|
MOV AL, 0x20
|
||||||
OUT 0x20, AL
|
OUT 0x20, AL
|
||||||
|
|
||||||
skip_eoi:
|
skip_eoi:
|
||||||
|
|
||||||
|
POPAD
|
||||||
; Enable interrupts
|
; Enable interrupts
|
||||||
STI
|
STI
|
||||||
|
|
||||||
; Clean up the stack
|
; Clean up the stack
|
||||||
ADD ESP, 8 ; Remove error code and interrupt number
|
ADD ESP, 16 ; Remove error code and interrupt number
|
||||||
|
|
||||||
; Restore context
|
|
||||||
POPAD ; Restore all general-purpose registers
|
|
||||||
|
|
||||||
; Return from interrupt
|
; Return from interrupt
|
||||||
IRETD
|
IRETD
|
@ -1,4 +1,5 @@
|
|||||||
#include "isr.h"
|
#include "isr.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
void isr_handler(struct isr_regs regs) {
|
void isr_handler(struct isr_regs regs) {
|
||||||
switch(regs.int_no) {
|
switch(regs.int_no) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define ISR_H
|
#define ISR_H
|
||||||
|
|
||||||
#include "../include/types.h"
|
#include "../include/types.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
enum ISR_Vector
|
enum ISR_Vector
|
||||||
{
|
{
|
||||||
@ -33,6 +34,7 @@ struct isr_regs
|
|||||||
esp_at_signal; // Pushed by the processor automatically
|
esp_at_signal; // Pushed by the processor automatically
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void isr_handler(struct isr_regs regs);
|
||||||
// Structure for storing register values during an ISR
|
// Structure for storing register values during an ISR
|
||||||
struct idt_regs
|
struct idt_regs
|
||||||
{
|
{
|
||||||
|
@ -1,43 +1,108 @@
|
|||||||
#include "kmalloc.h"
|
#include "kmalloc.h"
|
||||||
|
|
||||||
static void *kernel_heap_start; // Start of the kernel heap
|
static void *kernel_heap_start; // Start of the kernel heap
|
||||||
static void *kernel_heap_end; // End of the kernel heap
|
static void *kernel_heap_end; // End of the kernel heap
|
||||||
|
static struct memory_block *free_blocks = NULL;
|
||||||
|
|
||||||
void init_kernel_heap(void *start, void *end) {
|
struct memory_block
|
||||||
kernel_heap_start = start;
|
{
|
||||||
kernel_heap_end = end;
|
size_t size;
|
||||||
// Initialize the kernel heap here
|
struct memory_block *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
void mark_as_used_kernel(void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
// Since we're using a linked list, there's no specific action needed
|
||||||
|
// to mark the block itself as used (it's already removed from the list).
|
||||||
|
// However, you might want to perform additional bookkeeping for debugging
|
||||||
|
// or memory analysis purposes.
|
||||||
|
|
||||||
|
// Example: Update some metadata associated with the allocated block
|
||||||
|
// ((struct memory_block *)ptr)->used = true; // Assuming a 'used' flag
|
||||||
|
|
||||||
|
// This function can be potentially empty if you don't require specific
|
||||||
|
// actions for marking a block as used in the kernel.
|
||||||
}
|
}
|
||||||
|
|
||||||
void *kmalloc(size_t size) {
|
void mark_as_free_kernel(void *ptr)
|
||||||
if (kernel_heap_start == NULL || kernel_heap_end == NULL) {
|
{
|
||||||
// Kernel heap not initialized, cannot allocate
|
// Cast the pointer to a memory_block structure
|
||||||
return NULL;
|
struct memory_block *block = (struct memory_block *)ptr;
|
||||||
}
|
|
||||||
|
|
||||||
// Align the size to the word size for efficiency
|
// Add the block to the beginning of the free block list
|
||||||
size += sizeof(size_t) - 1;
|
block->next = free_blocks;
|
||||||
size &= ~(sizeof(size_t) - 1);
|
free_blocks = block;
|
||||||
|
|
||||||
// Search for a free block of sufficient size
|
|
||||||
// This is a placeholder for the actual algorithm
|
|
||||||
void *block = find_free_kernel_block(size);
|
|
||||||
if (block != NULL) {
|
|
||||||
// Mark the block as used
|
|
||||||
mark_as_used_kernel(block, size);
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No suitable block found, out of memory
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void kfree(void *ptr) {
|
void init_kernel_heap(void *start, void *end)
|
||||||
if (ptr == NULL) {
|
{
|
||||||
return;
|
kernel_heap_start = start;
|
||||||
}
|
kernel_heap_end = end;
|
||||||
|
|
||||||
// Mark the block as free
|
free_blocks = (struct memory_block *)start;
|
||||||
// This is a placeholder for the actual algorithm
|
free_blocks->size = (size_t)(end - start);
|
||||||
mark_as_free_kernel(ptr);
|
free_blocks->next = NULL;
|
||||||
|
|
||||||
|
// You'll need to replace this placeholder with logic to split the
|
||||||
|
// initial block into smaller ones based on your requirements.
|
||||||
|
}
|
||||||
|
|
||||||
|
void *find_free_kernel_block(size_t size)
|
||||||
|
{
|
||||||
|
// First-fit algorithm (can be optimized with different strategies)
|
||||||
|
struct memory_block *current = free_blocks;
|
||||||
|
while (current != NULL)
|
||||||
|
{
|
||||||
|
if (current->size >= size)
|
||||||
|
{
|
||||||
|
// Found a suitable block
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
// No suitable block found
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *kmalloc(size_t size)
|
||||||
|
{
|
||||||
|
if (kernel_heap_start == NULL || kernel_heap_end == NULL)
|
||||||
|
{
|
||||||
|
// Kernel heap not initialized, cannot allocate
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Align the size to the word size for efficiency
|
||||||
|
size += sizeof(size_t) - 1;
|
||||||
|
size &= ~(sizeof(size_t) - 1);
|
||||||
|
|
||||||
|
// Search for a free block of sufficient size
|
||||||
|
void *block = find_free_kernel_block(size);
|
||||||
|
if (block != NULL)
|
||||||
|
{
|
||||||
|
// Mark the block as used
|
||||||
|
mark_as_used_kernel(block, size);
|
||||||
|
|
||||||
|
// This is a placeholder for splitting the block if necessary
|
||||||
|
// You'll need to implement logic to potentially split the block
|
||||||
|
// into a used part of size 'size' and a remaining free block
|
||||||
|
// if the block size is significantly larger than 'size'.
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No suitable block found, out of memory
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kfree(void *ptr)
|
||||||
|
{
|
||||||
|
if (ptr == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the block as free
|
||||||
|
// This is a placeholder for the actual algorithm
|
||||||
|
mark_as_free_kernel(ptr);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#ifndef KMALLOC_H
|
#ifndef KMALLOC_H_ // Corrected guard macro
|
||||||
#define KMALLOC_H
|
#define KMALLOC_H_
|
||||||
|
|
||||||
#include <stddef.h> // For size_t
|
#include <stddef.h> // For size_t
|
||||||
|
|
||||||
void *kmalloc(size_t size);
|
void *kmalloc(size_t size);
|
||||||
void kfree(void *ptr);
|
void kfree(void *ptr);
|
||||||
|
|
||||||
#endif // KMALLOC_H
|
void mark_as_used_kernel(void *ptr, size_t size);
|
||||||
|
void mark_as_free_kernel(void *ptr);
|
||||||
|
|
||||||
|
#endif /* KMALLOC_H_ */
|
||||||
|
@ -10,6 +10,20 @@ void init_heap(void *start, void *end)
|
|||||||
// Initialize the heap here
|
// Initialize the heap here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *find_free_block(size_t size) {
|
||||||
|
// Implementation is similar to find_free_kernel_block
|
||||||
|
struct memory_block *current = free_blocks;
|
||||||
|
while (current != NULL) {
|
||||||
|
if (current->size >= size) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No suitable block found
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void *malloc(size_t size)
|
void *malloc(size_t size)
|
||||||
{
|
{
|
||||||
if (heap_start == NULL || heap_end == NULL)
|
if (heap_start == NULL || heap_end == NULL)
|
||||||
|
@ -6,4 +6,6 @@
|
|||||||
void *malloc(size_t size);
|
void *malloc(size_t size);
|
||||||
void free(void *ptr);
|
void free(void *ptr);
|
||||||
|
|
||||||
|
void *find_free_block(size_t size);
|
||||||
|
|
||||||
#endif // MALLOC_H
|
#endif // MALLOC_H
|
||||||
|
@ -1,3 +1,26 @@
|
|||||||
/*
|
#include <stdint.h>
|
||||||
timer functions go here
|
#include "timer.h"
|
||||||
*/
|
|
||||||
|
// Function to send a byte to an I/O port
|
||||||
|
void outb(uint16_t port, uint8_t data) {
|
||||||
|
__asm__ volatile ("outb %b, %w" : : "a" (data), "d" (port));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to initialize the timer with a desired frequency
|
||||||
|
void init_timer(uint32_t frequency) {
|
||||||
|
// Calculate the divisor based on the desired frequency
|
||||||
|
uint16_t divisor = 1193180 / frequency;
|
||||||
|
|
||||||
|
// Send the mode and divisor byte to the timer control port
|
||||||
|
outb(IOPORT_TIMER_CONTROL, TIMER_CTRL_SELECT_0 | TIMER_MODE_3 | TIMER_CTRL_BINARY);
|
||||||
|
outb(IOPORT_TIMER_0, (uint8_t) (divisor & 0xFF));
|
||||||
|
outb(IOPORT_TIMER_0, (uint8_t) ((divisor >> 8) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to wait for a specified number of microseconds
|
||||||
|
void wait_us(uint32_t microseconds) {
|
||||||
|
uint32_t count = microseconds * 10; // Adjust for desired resolution (1 count = 100ns)
|
||||||
|
while (count--) {
|
||||||
|
// Implement a short delay loop here (e.g., empty loop or reading the PIT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,37 @@
|
|||||||
/*
|
Here's a breakdown of how to create timer.c and timer.h compatible with the 8253 for x86 32-bit protected mode on 386/486 processors:
|
||||||
timer functions go here
|
|
||||||
*/
|
1. timer.h:
|
||||||
|
|
||||||
|
This header file will contain function prototypes, constants, and any data structures you might need for interacting with the 8253 timer. Here's an example:
|
||||||
|
C
|
||||||
|
|
||||||
|
#ifndef TIMER_H
|
||||||
|
#define TIMER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// I/O port addresses for the 8253
|
||||||
|
#define IOPORT_TIMER_CONTROL 0x43
|
||||||
|
#define IOPORT_TIMER_0 0x40
|
||||||
|
|
||||||
|
// Timer modes (refer to 8253 documentation for details)
|
||||||
|
#define TIMER_MODE_0 0x00
|
||||||
|
#define TIMER_MODE_1 0x40
|
||||||
|
#define TIMER_MODE_2 0x80
|
||||||
|
#define TIMER_MODE_3 0xC0
|
||||||
|
|
||||||
|
// Timer control bits (refer to 8253 documentation for details)
|
||||||
|
#define TIMER_CTRL_SELECT_0 0x01
|
||||||
|
#define TIMER_CTRL_SELECT_1 0x02
|
||||||
|
#define TIMER_CTRL_SELECT_2 0x04
|
||||||
|
#define TIMER_CTRL_MODE 0x30
|
||||||
|
#define TIMER_CTRL_BINARY 0x00
|
||||||
|
#define TIMER_CTRL_BCD 0x40
|
||||||
|
#define TIMER_CTRL_ONE_SHOT 0x80
|
||||||
|
#define TIMER_CTRL_CONTINOUS 0x00
|
||||||
|
|
||||||
|
// Function prototypes
|
||||||
|
void init_timer(uint32_t frequency);
|
||||||
|
void wait_us(uint32_t microseconds);
|
||||||
|
|
||||||
|
#endif /* TIMER_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user