This commit is contained in:
2025-05-28 14:41:02 -05:00
commit 6d366537dd
73 changed files with 4448 additions and 0 deletions

76
Makefile Normal file
View File

@ -0,0 +1,76 @@
# === Config ===
TARGET := boot/espresso.bin
ISO := boot/espresso.iso
CC := i686-elf-gcc
AS := i686-elf-as
NASM := nasm
QEMU_MKE_IMG := qemu-img create -f raw espresso.img 64M
NASMFLAGS := -f elf32
WNOFLAGS := -Wno-discarded-qualifiers
CFLAGS := -std=gnu99 -ffreestanding -O2 -Wall -Wextra -msse $(WNOFLAGS)
LDFLAGS := -T linker.ld -ffreestanding -O2 -nostdlib
QEMUFLAGS := -device isa-debug-exit -serial stdio -kernel $(TARGET) -drive file=espresso.img,format=raw,if=ide,readonly=off,rerror=report,werror=report -cpu qemu32,sse2 #-singlestep
SRC_DIRS := kernel drivers lib
INCLUDE_DIRS := include
INCLUDES := $(addprefix -I, $(INCLUDE_DIRS))
ISO_DIR := isodir
BOOT_DIR := $(ISO_DIR)/boot
GRUB_DIR := $(BOOT_DIR)/grub
GRUB_CFG := grub.cfg
# === File collection ===
C_SRCS := $(foreach dir, $(SRC_DIRS), $(shell find $(dir) -name '*.c'))
S_SRCS := boot.s crti.s crtn.s misc_asm.s
NASM_SRCS := idt.asm isr128.asm gdt.asm isr_stub_table.asm pit.asm
CRTI_OBJ := crti.o
CRTBEGIN_OBJ := $(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o)
CRTEND_OBJ := $(shell $(CC) $(CFLAGS) -print-file-name=crtend.o)
CRTN_OBJ := crtn.o
OBJS := $(C_SRCS:.c=.o) $(S_SRCS:.s=.o) $(NASM_SRCS:.asm=.o)
OBJ_LINK_LIST := $(CRTI_OBJ) $(CRTBEGIN_OBJ) $(OBJS) $(CRTEND_OBJ) $(CRTN_OBJ)
INTERNAL_OBJS := $(CRTI_OBJ) $(OBJS) $(CRTN_OBJ)
# === Build all targets ===
build: all iso run
# === Default target ===
all: $(TARGET)
# === Linking ===
$(TARGET): boot.o $(filter-out boot.o, $(OBJ_LINK_LIST))
@mkdir -p $(dir $@)
$(CC) $(LDFLAGS) -o $@ $^ -lgcc
# === Compiling C files ===
%.o: %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
# === Assembling S files ===
%.o: %.s
$(AS) $< -o $@
%.o: %.asm
$(NASM) $(NASMFLAGS) $< -o $@
# === ISO generation ===
iso: $(TARGET)
@grub-file --is-x86-multiboot $(TARGET) && echo "multiboot confirmed" || (echo "not multiboot" && exit 1)
mkdir -p $(GRUB_DIR)
cp $(TARGET) $(BOOT_DIR)/
cp $(GRUB_CFG) $(GRUB_DIR)/
grub-mkrescue -o $(ISO) $(ISO_DIR)
# === Run in QEMU ===
run: iso
$(QEMU_MKE_IMG)
qemu-system-i386 $(QEMUFLAGS)
# === Clean all build artifacts ===
clean:
rm -f $(INTERNAL_OBJS) $(TARGET) $(ISO)
rm -rf $(ISO_DIR)
rm -f espresso.img
.PHONY: all clean iso run

212
boot.s Normal file
View File

@ -0,0 +1,212 @@
/* Declare constants for the multiboot header. */
.set ALIGN, 1<<0 /* align loaded modules on page boundaries */
.set MEMINFO, 1<<1 /* provide memory map */
.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */
.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */
.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */
/*
Declare a multiboot header that marks the program as a kernel. These are magic
values that are documented in the multiboot standard. The bootloader will
search for this signature in the first 8 KiB of the kernel file, aligned at a
32-bit boundary. The signature is in its own section so the header can be
forced to be within the first 8 KiB of the kernel file.
*/
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
/*
The multiboot standard does not define the value of the stack pointer register
(esp) and it is up to the kernel to provide a stack. This allocates room for a
small stack by creating a symbol at the bottom of it, then allocating 16384
bytes for it, and finally creating a symbol at the top. The stack grows
downwards on x86. The stack is in its own section so it can be marked nobits,
which means the kernel file is smaller because it does not contain an
uninitialized stack. The stack on x86 must be 16-byte aligned according to the
System V ABI standard and de-facto extensions. The compiler will assume the
stack is properly aligned and failure to align the stack will result in
undefined behavior.
*/
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:
/*
The linker script specifies _start as the entry point to the kernel and the
bootloader will jump to this position once the kernel has been loaded. It
doesn't make sense to return from this function as the bootloader is gone.
*/
.section .text
.global _start
.global _kernel_early
/*.global loadPageDirectory
.global enablePaging*/
.type _start, @function
/*load_page_directory:
push %ebp
mov %esp, %ebp
mov 8(%esp), %eax
mov %eax, %cr3
mov %ebp, %esp
pop %ebp
ret
enable_paging:
push %ebp
mov %esp, %ebp
mov %cr0, %eax
or $0x80000000, %eax
mov %eax, %cr0
mov %ebp, %esp
pop %ebp
ret*/
enable_sse_asm:
push %eax
push %ebx
push %ecx
push %edx
# Check CPUID support
pushf
pop %eax
mov %eax, %ecx
xor $0x200000, %eax
push %eax
popf
pushf
pop %eax
xor %ecx, %eax
jz .no_cpuid
# Check for SSE
mov $1, %eax
cpuid
test $0x02000000, %edx
jz .no_sse
# Enable SSE
mov %cr0, %eax
and $~0x4, %eax # Clear EM (bit 2)
or $0x2, %eax # Set MP (bit 1)
mov %eax, %cr0
mov %cr4, %eax
or $0x600, %eax # Set OSFXSR | OSXMMEXCPT
mov %eax, %cr4
lea sse_initialized, %ebx
movl $1, (%ebx)
.no_sse:
.no_cpuid:
pop %edx
pop %ecx
pop %ebx
pop %eax
ret
_kernel_early:
call _init
/*
TODO: add more stuff here that needs to be ran before the main kernel code.
*/
ret
_start:
/*
The bootloader has loaded us into 32-bit protected mode on a x86
machine. Interrupts are disabled. Paging is disabled. The processor
state is as defined in the multiboot standard. The kernel has full
control of the CPU. The kernel can only make use of hardware features
and any code it provides as part of itself. There's no printf
function, unless the kernel provides its own <stdio.h> header and a
printf implementation. There are no security restrictions, no
safeguards, no debugging mechanisms, only what the kernel provides
itself. It has absolute and complete power over the
machine.
*/
/*
To set up a stack, we set the esp register to point to the top of the
stack (as it grows downwards on x86 systems). This is necessarily done
in assembly as languages such as C cannot function without a stack.
*/
movl $stack_top, %esp
andl $0xFFFFFFF0, %esp
movl %esp, %ebp
/*
This is a good place to initialize crucial processor state before the
high-level kernel is entered. It's best to minimize the early
environment where crucial features are offline. Note that the
processor is not fully initialized yet: Features such as floating
point instructions and instruction set extensions are not initialized
yet. The GDT should be loaded here. Paging should be enabled here.
C++ features such as global constructors and exceptions will require
runtime support to work as well.
*/
cli /* Just in case */
call enable_sse_asm
push %eax
push %ebx
/*
Call _kernel_early, early low-level initialization will happen there;
please note that while _kernel_early is written in assembler,
kernel_early is written in C. (kernel_early is called by _kernel_early, don't be confused. ;) )
*/
call _kernel_early
/*
Enter the high-level kernel. The ABI requires the stack is 16-byte
aligned at the time of the call instruction (which afterwards pushes
the return pointer of size 4 bytes). The stack was originally 16-byte
aligned above and we've pushed a multiple of 16 bytes to the
stack since (pushed 0 bytes so far), so the alignment has thus been
preserved and the call is well defined.
*/
call kernel_main
/*
If the system has nothing more to do, put the computer into an
infinite loop. To do that:
1) Disable interrupts with cli (clear interrupt enable in eflags).
They are already disabled by the bootloader, so this is not needed.
Mind that you might later enable interrupts and return from
kernel_main (which is sort of nonsensical to do).
2) Wait for the next interrupt to arrive with hlt (halt instruction).
Since they are disabled, this will lock up the computer.
3) Jump to the hlt instruction if it ever wakes up due to a
non-maskable interrupt occurring or due to system management mode.
*/
cli
1: hlt
jmp 1b
/*
Set the size of the _start symbol to the current location '.' minus its start.
This is useful when debugging or when you implement call tracing.
*/
.size _start, . - _start
.section .data
.global sse_initialized
sse_initialized: .word 0

15
crti.s Normal file
View File

@ -0,0 +1,15 @@
.section .init
.global _init
.type _init, @function
_init:
push %ebp
movl %esp, %ebp
/* gcc will nicely put the contents of crtbegin.o's .init section here. */
.section .fini
.global _fini
.type _fini, @function
_fini:
push %ebp
movl %esp, %ebp
/* gcc will nicely put the contents of crtbegin.o's .fini section here. */

9
crtn.s Normal file
View File

@ -0,0 +1,9 @@
.section .init
/* gcc will nicely put the contents of crtend.o's .init section here. */
popl %ebp
ret
.section .fini
/* gcc will nicely put the contents of crtend.o's .fini section here. */
popl %ebp
ret

View File

@ -0,0 +1,7 @@
Notes about the PS/2 keyboard driver.
the actual key-codes are not what you might expect.
what I mean is, what does 0x3F mean? what key was pressed?
The keyset used by Espresso is pretty much the ASCII code set.
Minus a few codes, some are replaced with other functions.

1
drivers/ahci.c Normal file
View File

@ -0,0 +1 @@
#include <drivers/ahci.h>

29
drivers/ehci.c Normal file
View File

@ -0,0 +1,29 @@
#include <stdint.h>
#define EHCI_BASE_ADDR 0xF0000000 // Example, your base address will be different
// EHCI Register Offsets (from EHCI spec)
#define EHCI_CAPLENGTH_OFFSET 0x00
#define EHCI_HCIVERSION_OFFSET 0x02
#define EHCI_CONTROL_OFFSET 0x04
#define EHCI_STATUS_OFFSET 0x08
#define EHCI_COMMAND_OFFSET 0x10
// Registers access
uint32_t read_ehci_register(uint32_t* base, uint32_t offset) {
return *(volatile uint32_t*)(base + offset);
}
void write_ehci_register(uint32_t* base, uint32_t offset, uint32_t value) {
*(volatile uint32_t*)(base + offset) = value;
}
// Initialize EHCI controller
void initialize_ehci(uint32_t* base) {
uint32_t control = read_ehci_register(base, EHCI_CONTROL_OFFSET);
control |= (1 << 1); // Enable controller
write_ehci_register(base, EHCI_CONTROL_OFFSET, control);
// Set up frame list pointer and other initialization steps as per EHCI specs
}

220
drivers/fs/duckfs.c Normal file
View File

@ -0,0 +1,220 @@
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <drivers/ide.h>
#include <fs/duckfs.h>
static uint16_t duckfs_initialized = 0;
static duckfs_file_header_t duckfs_root;
const char* duckfs_versions[18] = { DFS_VERSION_0, DFS_VERSION_1, DFS_VERSION_2, DFS_VERSION_3, DFS_VERSION_4 };
int32_t duckfs_init(int16_t drive)
{
char duckfs_header_block[512];
if (ide_read48(drive, 0xA, 1, duckfs_header_block) != 0)
{
printf("[ DEBUG ] Disk read error on drive #%i\n", drive);
return -1;
}
duckfs_superblock_t* superblock = (duckfs_superblock_t*)duckfs_header_block;
/*
if (superblock->duckfs_magic != (int32_t) DFS_MAGIC)
{
return -2;
}
bool compliant = false;
for (int16_t i = 0; i < 5; ++i)
{
if (strcmp(superblock->duckfs_version_string, duckfs_versions[i]) == 0)
{
compliant = true;
}
}
if (compliant == false)
{
return -3;
}*/
memset(&duckfs_root, 0, sizeof(duckfs_root));
strncpy(duckfs_root.filename, "/", DFS_MAX_FILENAME_LEN);
strncpy(duckfs_root.permissions, "RW", sizeof(duckfs_root.permissions));
duckfs_root.num_sectors = -1;
duckfs_root.type = DFS_FILE_DIRECTORY;
duckfs_root.next = NULL;
duckfs_root.prev = NULL;
duckfs_root.contents = NULL;
duckfs_root.lba_start = 0;
duckfs_root.lba_end = 0;
duckfs_root.contents = malloc(sizeof(duckfs_file_header_t) * DFS_MAX_FILES);
if (!duckfs_root.contents) {
printf("Memory allocation failed for duckfs_root.contents\n");
return -4;
}
for (int32_t i = 0; i < DFS_MAX_FILES; i++) {
duckfs_root.contents[i].type = DFS_FILE_UNUSED;
}
duckfs_initialized = 1;
return 0;
}
void duckfs_format(int16_t drive)
{
/* Nothing to do, DuckFS does not require formatting. */
}
int32_t duckfs_makedir(const char* filename, const char* perms)
{
duckfs_file_header_t* dir = malloc(sizeof(duckfs_file_header_t));
if (!dir)
{
return -1;
}
if (strlen(perms) < 3)
{
return -2;
}
char p[3] = { "." };
p[0] = perms[0];
p[1] = perms[1];
p[2] = perms[2];
char filen[DFS_MAX_FILENAME_LEN + 1];
char perma[3];
strcpy(filen, filename);
strcpy(perma, perms);
strncpy(dir->filename, filen, sizeof(dir->filename) - 1);
dir->filename[sizeof(dir->filename) - 1] = '\0';
strncpy(dir->permissions, p, sizeof(dir->permissions));
dir->type = DFS_FILE_DIRECTORY;
dir->contents = malloc(sizeof(duckfs_file_header_t) * DFS_MAX_FILES);
if (!dir->contents) {
free(dir);
return -1;
}
for (int i = 0; i < DFS_MAX_FILES; i++) {
dir->contents[i].type = DFS_FILE_UNUSED;
}
return 0;
}
void duckfs_free_directory(duckfs_file_header_t* dir)
{
if (!dir || dir->type != DFS_FILE_DIRECTORY || !dir->contents)
{
return;
}
for (int i = 0; i < DFS_MAX_FILES; i++) {
duckfs_file_header_t* entry = &dir->contents[i];
if (entry->type == DFS_FILE_UNUSED)
{
continue;
}
if (entry->type == DFS_FILE_DIRECTORY) {
duckfs_free_directory(entry);
}
}
free(dir->contents);
dir->contents = NULL;
}
bool duckfs_add_entry(duckfs_file_header_t* dir, duckfs_file_header_t* entry)
{
if (dir->type != DFS_FILE_DIRECTORY || !dir->contents)
{
return false;
}
for (int i = 0; i < DFS_MAX_FILES; i++) {
if (dir->contents[i].type == DFS_FILE_UNUSED) {
dir->contents[i] = *entry;
return true;
}
}
return false; /* Directory full */
}
duckfs_file_header_t duckfs_create_entry(const char* name, const char* perms, const char* type)
{
duckfs_file_header_t entry = {0};
strncpy(entry.filename, name, DFS_MAX_FILENAME_LEN);
strncpy(entry.permissions, perms, sizeof(entry.permissions) - 1);
int16_t itype = DFS_FILE_UNUSED;
if (strcmp(type, "text") == 0)
{
itype = DFS_FILE_TEXT;
}
else if (strcmp(type, "bin") == 0)
{
itype = DFS_FILE_BINARY;
}
else if (strcmp(type, "dir") == 0)
{
itype = DFS_FILE_BINARY;
}
else
{
duckfs_file_header_t vvvv = { .type = DFS_FILE_ERROR };
return vvvv;
}
entry.num_sectors = 0;
entry.type = itype;
entry.next = NULL;
entry.prev = NULL;
entry.contents = NULL;
entry.lba_start = 0;
entry.lba_end = 0;
if (itype == DFS_FILE_DIRECTORY)
{
entry.contents = malloc(sizeof(duckfs_file_header_t) * DFS_MAX_FILES);
if (entry.contents)
{
for (int i = 0; i < DFS_MAX_FILES; i++)
{
entry.contents[i].type = DFS_FILE_UNUSED;
}
}
}
return entry;
}
duckfs_file_header_t duckfs_makefile(const char* filename, const char* perms, duckfs_file_header_t parent)
{
}

428
drivers/fs/fat32.c Normal file
View File

@ -0,0 +1,428 @@
#include <string.h>
#include <stdio.h>
#include <drivers/ide.h>
#include <fs/fat32.h>
struct __attribute__((packed)) fat32_bpb {
uint8_t jmp[3];
uint8_t oem[8];
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sector_count;
uint8_t num_fats;
uint16_t root_entry_count;
uint16_t total_sectors_16;
uint8_t media;
uint16_t fat_size_16;
uint16_t sectors_per_track;
uint16_t num_heads;
uint32_t hidden_sectors;
uint32_t total_sectors_32;
uint32_t fat_size_32;
uint16_t ext_flags;
uint16_t fs_version;
uint32_t root_cluster;
};
static int32_t fat32_drive = 0;
static struct fat32_bpb bpb;
static uint32_t fat_start_lba;
static uint32_t cluster_heap_lba;
static uint32_t current_directory_cluster;
static uint8_t sector[512];
int32_t fat32_init(int32_t drive)
{
fat32_drive = drive;
int err = ide_read48(drive, 0, 1, sector);
if (err != 0) {
printf("ide_read48 failed on sector 0 (err = %d)\n", err);
return -1;
}
memcpy(&bpb, sector, sizeof(bpb));
fat_start_lba = bpb.reserved_sector_count;
cluster_heap_lba = fat_start_lba + bpb.num_fats * bpb.fat_size_32;
current_directory_cluster = bpb.root_cluster;
printf("Bytes per sector: %u\n", bpb.bytes_per_sector);
printf("Sectors per cluster: %u\n", bpb.sectors_per_cluster);
printf("Reserved sectors: %u\n", bpb.reserved_sector_count);
printf("Number of FATs: %u\n", bpb.num_fats);
printf("FAT size (32): %u\n", bpb.fat_size_32);
printf("Root cluster: %u\n", bpb.root_cluster);
return 0;
}
static uint32_t cluster_to_lba(uint32_t cluster)
{
return cluster_heap_lba + (cluster - 2) * bpb.sectors_per_cluster;
}
static void format_83_name(const char *input, char out[11]) {
memset(out, ' ', 11);
int i = 0, j = 0;
// Copy name part (up to 8 chars)
while (input[i] && input[i] != '.' && j < 8) {
out[j++] = toupper((unsigned char)input[i++]);
}
// Skip dot
if (input[i] == '.') i++;
// Copy extension (up to 3 chars)
int k = 8;
while (input[i] && j < 11) {
out[k++] = toupper((unsigned char)input[i++]);
j++;
}
}
static uint32_t find_free_cluster() {
// Read FAT sectors one by one
for (uint32_t cluster = 2; cluster < bpb.total_sectors_32; cluster++) {
uint32_t fat_sector = fat_start_lba + (cluster * 4) / 512;
uint32_t fat_offset = (cluster * 4) % 512;
if (ide_read48(fat32_drive, fat_sector, 1, sector) != 0)
return 0;
uint32_t entry = *(uint32_t *)(sector + fat_offset) & 0x0FFFFFFF;
if (entry == 0x00000000) {
return cluster;
}
}
return 0; // No free cluster found
}
int32_t fat32_list_root(void)
{
uint32_t lba = cluster_to_lba(current_directory_cluster);
if (ide_read48(fat32_drive, lba, 1, sector) != 0)
{
return -1;
}
for (int i = 0; i < 512; i += 32)
{
char *entry = (char *)&sector[i];
if ((uint8_t)entry[0] == 0x00)
{
break;
}
if ((uint8_t)entry[0] == 0xE5)
{
continue;
}
char name[12];
memcpy(name, entry, 11);
name[11] = 0;
for (int j = 0; j < 11; j++)
{
if (name[j] == ' ')
{
name[j] = 0;
}
}
uint8_t attr = entry[11];
if (attr & FAT32_ATTR_DIRECTORY)
{
printf("<DIR> %s\n", name);
}
else
{
printf(" %s\n", name);
}
}
return 0;
}
int32_t fat32_read_file(const char *name, uint8_t *buffer, uint32_t max_len)
{
uint32_t lba = cluster_to_lba(current_directory_cluster);
if (ide_read48(fat32_drive, lba, 1, sector) != 0)
{
return -1;
}
for (int32_t i = 0; i < 512; i += 32)
{
char *entry = (char *)&sector[i];
if ((uint8_t)entry[0] == 0x00)
{
break;
}
if ((uint8_t)entry[0] == 0xE5)
{
continue;
}
char entry_name[12];
memcpy(entry_name, entry, 11);
entry_name[11] = 0;
for (int32_t j = 0; j < 11; j++)
{
if (entry_name[j] == ' ')
{
entry_name[j] = 0;
}
}
if (strncmp(entry_name, name, 11) != 0)
{
continue;
}
uint32_t cluster = (*(uint16_t *)(entry + 26)) | ((*(uint16_t *)(entry + 20)) << 16);
uint32_t size = *(uint32_t *)(entry + 28);
uint32_t bytes_read = 0;
while (cluster < 0x0FFFFFF8 && bytes_read < max_len && bytes_read < size)
{
uint32_t lba = cluster_to_lba(cluster);
if (ide_read48(fat32_drive, lba, bpb.sectors_per_cluster, buffer + bytes_read) != 0)
{
return -1;
}
bytes_read += bpb.sectors_per_cluster * 512;
uint32_t fat_sector = cluster * 4 / 512;
uint32_t fat_offset = cluster * 4 % 512;
if (ide_read48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0)
{
return -1;
}
cluster = *(uint32_t *)(sector + fat_offset) & 0x0FFFFFFF;
}
return bytes_read;
}
return -2;
}
int32_t fat32_write_file(const char *name, const uint8_t *buffer, uint32_t len)
{
uint32_t lba = cluster_to_lba(current_directory_cluster);
if (ide_read48(fat32_drive, lba, 1, sector) != 0)
{
return -1;
}
for (int32_t i = 0; i < 512; i += 32)
{
char *entry = (char *)&sector[i];
if ((uint8_t)entry[0] == 0x00) break;
if ((uint8_t)entry[0] == 0xE5) continue;
char entry_name[12];
memcpy(entry_name, entry, 11);
entry_name[11] = 0;
for (int j = 0; j < 11; j++)
{
if (entry_name[j] == ' ')
{
entry_name[j] = 0;
}
}
if (strncmp(entry_name, name, 11) != 0)
{
continue;
}
uint32_t cluster = (*(uint16_t *)(entry + 26)) | ((*(uint16_t *)(entry + 20)) << 16);
uint32_t bytes_written = 0;
while (cluster < 0x0FFFFFF8 && bytes_written < len)
{
uint32_t lba = cluster_to_lba(cluster);
if (ide_write48(fat32_drive, lba, bpb.sectors_per_cluster, buffer + bytes_written) != 0)
{
return -1;
}
bytes_written += bpb.sectors_per_cluster * 512;
if (bytes_written < len)
{
uint32_t fat_sector = cluster * 4 / 512;
uint32_t fat_offset = cluster * 4 % 512;
if (ide_read48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0)
{
return -1;
}
uint32_t next_cluster = find_free_cluster();
if (next_cluster == 0)
{
return -3;
}
*(uint32_t *)(sector + fat_offset) = next_cluster & 0x0FFFFFFF;
if (ide_write48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0)
{
return -1;
}
cluster = next_cluster;
}
else
{
uint32_t fat_sector = cluster * 4 / 512;
uint32_t fat_offset = cluster * 4 % 512;
if (ide_read48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0)
{
return -1;
}
*(uint32_t *)(sector + fat_offset) = 0x0FFFFFFF;
if (ide_write48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0)
{
return -1;
}
break;
}
}
*(uint32_t *)(entry + 28) = len;
if (ide_write48(fat32_drive, lba, 1, sector) != 0)
{
return -1;
}
return bytes_written;
}
return -2;
}
int32_t fat32_create_file(const char *name) {
uint32_t root_lba = cluster_to_lba(bpb.root_cluster);
// Read root directory cluster sector (assuming 1 sector here for simplicity)
if (ide_read48(fat32_drive, root_lba, 1, sector) != 0)
return -1;
char formatted_name[11];
format_83_name(name, formatted_name);
for (int32_t i = 0; i < 512; i += 32) {
uint8_t *entry = &sector[i];
if (entry[0] == 0x00 || entry[0] == 0xE5) {
// Found free directory entry, clear it
memset(entry, 0, 32);
// Copy formatted filename
memcpy(entry, formatted_name, 11);
// Set attributes to Archive (normal file)
entry[11] = 0x20;
// Allocate first cluster for the file
uint32_t free_cluster = find_free_cluster();
if (free_cluster == 0) {
return -2; // No free cluster
}
// Mark cluster as end-of-chain in FAT
uint32_t fat_sector = fat_start_lba + (free_cluster * 4) / 512;
uint32_t fat_offset = (free_cluster * 4) % 512;
if (ide_read48(fat32_drive, fat_sector, 1, sector) != 0)
return -1;
*(uint32_t *)(sector + fat_offset) = 0x0FFFFFFF; // end of cluster chain
if (ide_write48(fat32_drive, fat_sector, 1, sector) != 0)
return -1;
// Set first cluster in directory entry (split high and low 16 bits)
entry[20] = (free_cluster >> 16) & 0xFF;
entry[21] = (free_cluster >> 24) & 0xFF;
entry[26] = free_cluster & 0xFF;
entry[27] = (free_cluster >> 8) & 0xFF;
// Set file size = 0
entry[28] = 0;
entry[29] = 0;
entry[30] = 0;
entry[31] = 0;
// Write back directory sector
if (ide_write48(fat32_drive, root_lba, 1, sector) != 0)
return -1;
return 0; // Success
}
}
return -3; // No free directory entry found
}
int32_t fat32_create_directory(const char *name) {
uint32_t lba = cluster_to_lba(current_directory_cluster);
if (ide_read48(fat32_drive, lba, 1, sector) != 0)
return -1;
for (int32_t i = 0; i < 512; i += 32) {
char *entry = (char *)&sector[i];
if ((uint8_t)entry[0] == 0x00 || (uint8_t)entry[0] == 0xE5) {
memset(entry, 0, 32);
memcpy(entry, name, strlen(name) > 11 ? 11 : strlen(name));
entry[11] = FAT32_ATTR_DIRECTORY;
uint32_t cluster = 6;
*(uint16_t *)(entry + 26) = cluster & 0xFFFF;
*(uint16_t *)(entry + 20) = (cluster >> 16) & 0xFFFF;
*(uint32_t *)(entry + 28) = 0;
if (ide_write48(fat32_drive, lba, 1, sector) != 0)
return -1;
return 0;
}
}
return -2;
}
int32_t fat32_change_directory(const char *name) {
uint32_t lba = cluster_to_lba(current_directory_cluster);
if (ide_read48(fat32_drive, lba, 1, sector) != 0)
return -1;
for (int32_t i = 0; i < 512; i += 32) {
char *entry = (char *)&sector[i];
if ((uint8_t)entry[0] == 0x00) break;
if ((uint8_t)entry[0] == 0xE5) continue;
char entry_name[12];
memcpy(entry_name, entry, 11);
entry_name[11] = 0;
for (int32_t j = 0; j < 11; j++)
if (entry_name[j] == ' ') entry_name[j] = 0;
if (strncmp(entry_name, name, 11) != 0) continue;
if (!(entry[11] & FAT32_ATTR_DIRECTORY))
return -2;
current_directory_cluster = (*(uint16_t *)(entry + 26)) | ((*(uint16_t *)(entry + 20)) << 16);
return 0;
}
return -3;
}

34
drivers/gdt.c Normal file
View File

@ -0,0 +1,34 @@
#include <gdt.h>
#define GDT_ENTRIES 3
struct gdt_entry gdt[GDT_ENTRIES];
struct gdt_ptr gp;
extern void gdt_flush(uint32_t);
void gdt_install()
{
gp.limit = (sizeof(struct gdt_entry) * GDT_ENTRIES) - 1;
gp.base = (uint32_t)&gdt;
gdt_set_entry(0, 0, 0, 0, 0); /* Null segment */
gdt_set_entry(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); /* Code segment */
gdt_set_entry(2, 0, 0xFFFFFFFF, 0x92, 0xCF); /* Data segment */
gdt_flush((uint32_t)&gp);
}
void gdt_set_entry(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran)
{
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = (limit >> 16) & 0x0F;
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access = access;
}

173
drivers/ide.c Normal file
View File

@ -0,0 +1,173 @@
#include <stdio.h>
#include <port_io.h>
#include <drivers/ide.h>
static void io_delay() {
for (int32_t i = 0; i < 1000; i++) {
outb(0x80, 0);
}
}
static int32_t ide_wait(uint16_t io, int32_t check_drq) {
for (int32_t i = 0; i < 5000; i++) {
uint8_t status = inb(io + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY)) {
if (!check_drq || (status & ATA_SR_DRQ))
return 0;
}
io_delay(1);
}
return 1;
}
static int32_t ide_wait_ready(uint16_t io) {
for (int32_t i = 0; i < 100000; i++) {
uint8_t status = inb(io + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY)) return 0;
}
return 1;
}
int32_t ide_identify(uint8_t drive, uint16_t* buffer) {
uint16_t io = ATA_PRIMARY_IO;
uint8_t slave_bit = (drive & 1) << 4;
ide_wait(io, 0);
outb(io + ATA_REG_HDDEVSEL, 0xA0 | slave_bit);
outb(io + ATA_REG_SECCOUNT0, 0);
outb(io + ATA_REG_LBA0, 0);
outb(io + ATA_REG_LBA1, 0);
outb(io + ATA_REG_LBA2, 0);
outb(io + ATA_REG_COMMAND, 0xEC); // IDENTIFY DEVICE
uint8_t status = inb(io + ATA_REG_STATUS);
if (status == 0) return -1; // No device
if (ide_wait(io, 1)) return -2;
insw(io + ATA_REG_DATA, buffer, 256); // Read 512 bytes
return 0;
}
void ide_initialize(void) {
outb(ATA_PRIMARY_CTRL, 0x02); // Disable IRQs
uint16_t identify_buf[256];
if (ide_identify(0, identify_buf) == 0) {
char model[41];
for (int i = 0; i < 20; ++i) {
model[i * 2 + 0] = identify_buf[27 + i] >> 8;
model[i * 2 + 1] = identify_buf[27 + i] & 0xFF;
}
model[40] = 0;
printf("Disk model: %s\n", model);
}
}
int32_t ide_read48(uint8_t drive, uint64_t lba, uint8_t sector_count, void* buffer) {
if (sector_count == 0) return -1;
uint16_t io = ATA_PRIMARY_IO;
uint8_t slave_bit = (drive & 1) << 4;
ide_wait(io, 0); // Wait for BSY to clear
outb(io + ATA_REG_HDDEVSEL, 0xE0 | slave_bit); // Select drive
// High bytes (use 0xC2 - 0xC6 offsets if you define them separately)
outb(io + 6, (sector_count >> 8) & 0xFF); // Sector Count High
outb(io + 7, (lba >> 40) & 0xFF); // LBA[47:40]
outb(io + 8, (lba >> 32) & 0xFF); // LBA[39:32]
outb(io + 9, (lba >> 24) & 0xFF); // LBA[31:24]
// Low bytes
outb(io + 2, sector_count & 0xFF); // Sector Count Low
outb(io + 3, (lba >> 0) & 0xFF); // LBA[7:0]
outb(io + 4, (lba >> 8) & 0xFF); // LBA[15:8]
outb(io + 5, (lba >> 16) & 0xFF); // LBA[23:16]
outb(io + ATA_REG_COMMAND, ATA_CMD_READ_SECTORS_EXT);
uint16_t* ptr = (uint16_t*)buffer;
for (int i = 0; i < sector_count; i++) {
if (ide_wait(io, 1)) {
uint8_t status = inb(io + ATA_REG_STATUS);
printf("IDE DRQ wait failed (read), status = 0x%02X\n", status);
return -2;
}
uint8_t status = inb(io + ATA_REG_STATUS);
if (status & ATA_SR_ERR) return -3;
insw(io + ATA_REG_DATA, ptr, 256);
ptr += 256;
}
return 0;
}
int32_t ide_write48(uint8_t drive, uint64_t lba, uint8_t sector_count, const void* buffer) {
if (sector_count == 0) return -1;
uint16_t io = ATA_PRIMARY_IO;
uint8_t slave_bit = (drive & 1) << 4;
ide_wait(io, 0); // Wait for BSY to clear
outb(io + ATA_REG_HDDEVSEL, 0xE0 | slave_bit); // Select drive
// Send high bytes of sector count and LBA first
outb(io + ATA_REG_SECCOUNT0, 0); // Sector count high byte
outb(io + ATA_REG_LBA0, (lba >> 40) & 0xFF); // LBA[47:40]
outb(io + ATA_REG_LBA1, (lba >> 32) & 0xFF); // LBA[39:32]
outb(io + ATA_REG_LBA2, (lba >> 24) & 0xFF); // LBA[31:24]
// Send low bytes
outb(io + ATA_REG_SECCOUNT0, sector_count); // Sector count low byte
outb(io + ATA_REG_LBA0, (lba >> 0) & 0xFF); // LBA[7:0]
outb(io + ATA_REG_LBA1, (lba >> 8) & 0xFF); // LBA[15:8]
outb(io + ATA_REG_LBA2, (lba >> 16) & 0xFF); // LBA[23:16]
// Send write command
outb(io + ATA_REG_COMMAND, ATA_CMD_WRITE_SECTORS_EXT); // 0x34
const uint16_t* ptr = (const uint16_t*)buffer;
for (int i = 0; i < sector_count; i++) {
if (ide_wait(io, 1)) {
uint8_t status = inb(io + ATA_REG_STATUS);
printf("IDE DRQ wait failed (write), status = 0x%02X\n", status);
return -2;
}
// Check for drive error
uint8_t status = inb(io + ATA_REG_STATUS);
if (status & ATA_SR_ERR) return -3;
outsw(io + ATA_REG_DATA, ptr, 256); // 256 words = 512 bytes
inb(io + ATA_REG_STATUS); /* Dummy read */
ptr += 256;
}
// Wait for BSY=0 before issuing FLUSH CACHE
if (ide_wait_ready(io)) return -5;
outb(io + ATA_REG_COMMAND, 0xE7); // FLUSH CACHE
if (ide_wait(io, 0)) {
printf("FLUSH CACHE timeout, status=0x%02X\n", inb(io + ATA_REG_STATUS));
return -4;
}
return 0;
}

131
drivers/idt.c Normal file
View File

@ -0,0 +1,131 @@
#include <string.h>
#include <port_io.h>
#include <idt.h>
#define IDT_ENTRIES 256
struct idt_entry idt[IDT_ENTRIES];
struct idt_ptr idtp;
extern void idt_load(uint32_t);
void idt_install(void)
{
idtp.limit = sizeof(struct idt_entry) * IDT_ENTRIES - 1;
idtp.base = (uint32_t)&idt;
memset(&idt, 0, sizeof(struct idt_entry) * IDT_ENTRIES);
/* Set entries for IRQs/ISRs here using `idt_set_entry(...)` */
/* Example: idt_set_entry(0, (uint32_t)isr0, 0x08, 0x8E); */
idt_load((uint32_t)&idtp);
}
void pic_remap(void)
{
outb(0x20, 0x11); /* Start init for master PIC */
outb(0xA0, 0x11); /* Start init for slave PIC */
outb(0x21, 0x20); /* Master PIC vector offset */
outb(0xA1, 0x28); /* Slave PIC vector offset */
outb(0x21, 0x04); /* Tell Master PIC about Slave PIC at IRQ2 */
outb(0xA1, 0x02); /* Tell Slave PIC its cascade identity */
outb(0x21, 0x01); /* 8086/88 mode */
outb(0xA1, 0x01);
outb(0x21, 0x0); /* Unmask master */
outb(0xA1, 0x0); /* Unmask slave */
}
void idt_set_entry(int num, uint32_t base, uint16_t sel, uint8_t flags)
{
idt[num].base_low = base & 0xFFFF;
idt[num].base_high = (base >> 16) & 0xFFFF;
idt[num].sel = sel;
idt[num].always0 = 0;
idt[num].flags = flags;
}
extern void IRQ0_handler(void);
extern void isr0(void);
extern void isr1(void);
extern void isr2(void);
extern void isr3(void);
extern void isr4(void);
extern void isr5(void);
extern void isr6(void);
extern void isr7(void);
extern void isr8(void);
extern void isr9(void);
extern void isr10(void);
extern void isr11(void);
extern void isr12(void);
extern void isr13(void);
extern void isr14(void);
extern void isr15(void);
extern void isr16(void);
extern void isr17(void);
extern void isr18(void);
extern void isr19(void);
extern void isr20(void);
extern void isr21(void);
extern void isr22(void);
extern void isr23(void);
extern void isr24(void);
extern void isr25(void);
extern void isr26(void);
extern void isr27(void);
extern void isr28(void);
extern void isr29(void);
extern void isr30(void);
extern void isr31(void);
extern void isr33(void);
extern void isr128(void);
void idt_install_isrs(void)
{
idt_set_entry(0, (uint32_t)isr0, 0x08, 0x8E);
idt_set_entry(1, (uint32_t)isr1, 0x08, 0x8E);
idt_set_entry(2, (uint32_t)isr2, 0x08, 0x8E);
idt_set_entry(3, (uint32_t)isr3, 0x08, 0x8E);
idt_set_entry(4, (uint32_t)isr4, 0x08, 0x8E);
idt_set_entry(5, (uint32_t)isr5, 0x08, 0x8E);
idt_set_entry(6, (uint32_t)isr6, 0x08, 0x8E);
idt_set_entry(7, (uint32_t)isr7, 0x08, 0x8E);
idt_set_entry(8, (uint32_t)isr8, 0x08, 0x8E);
idt_set_entry(9, (uint32_t)isr9, 0x08, 0x8E);
idt_set_entry(10, (uint32_t)isr10, 0x08, 0x8E);
idt_set_entry(11, (uint32_t)isr11, 0x08, 0x8E);
idt_set_entry(12, (uint32_t)isr12, 0x08, 0x8E);
idt_set_entry(13, (uint32_t)isr13, 0x08, 0x8E);
idt_set_entry(14, (uint32_t)isr14, 0x08, 0x8E);
idt_set_entry(15, (uint32_t)isr15, 0x08, 0x8E);
idt_set_entry(16, (uint32_t)isr16, 0x08, 0x8E);
idt_set_entry(17, (uint32_t)isr17, 0x08, 0x8E);
idt_set_entry(18, (uint32_t)isr18, 0x08, 0x8E);
idt_set_entry(19, (uint32_t)isr19, 0x08, 0x8E);
idt_set_entry(20, (uint32_t)isr20, 0x08, 0x8E);
idt_set_entry(21, (uint32_t)isr21, 0x08, 0x8E);
idt_set_entry(22, (uint32_t)isr22, 0x08, 0x8E);
idt_set_entry(23, (uint32_t)isr23, 0x08, 0x8E);
idt_set_entry(24, (uint32_t)isr24, 0x08, 0x8E);
idt_set_entry(25, (uint32_t)isr25, 0x08, 0x8E);
idt_set_entry(26, (uint32_t)isr26, 0x08, 0x8E);
idt_set_entry(27, (uint32_t)isr27, 0x08, 0x8E);
idt_set_entry(28, (uint32_t)isr28, 0x08, 0x8E);
idt_set_entry(29, (uint32_t)isr29, 0x08, 0x8E);
idt_set_entry(30, (uint32_t)isr30, 0x08, 0x8E);
idt_set_entry(31, (uint32_t)isr31, 0x08, 0x8E);
idt_set_entry(33, (uint32_t)isr33, 0x08, 0x8E);
}
void idt_install_syscall(void)
{
idt_set_entry(128, (uint32_t)isr128, 0x08, 0xEE);
}

42
drivers/isr.c Normal file
View File

@ -0,0 +1,42 @@
#include <port_io.h>
#include <stdio.h>
#include <isr.h>
void (*irq_handlers[256])(isr_stack_t *r) = { 0 };
void register_interrupt_handler(uint8_t n, void (*handler)(isr_stack_t *))
{
irq_handlers[n] = handler;
}
void isr_handler(isr_stack_t *r)
{
if (irq_handlers[r->int_no]) {
irq_handlers[r->int_no](r);
} else {
printf("Unhandled interrupt: %d\n", r->int_no);
}
/* Send EOI to PIC */
if (r->int_no >= 40)
{
outb(0xA0, 0x20);
}
if (r->int_no >= 32)
{
outb(0x20, 0x20);
}
}
void syscall_handler(regs_t *r)
{
switch (r->eax) {
case 0: printf("Syscall: Hello world\n"); break;
case 1: printf("Syscall: value = %d\n", r->ebx); break;
default: printf("Unknown syscall %d\n", r->eax);
}
}

59
drivers/pci.c Normal file
View File

@ -0,0 +1,59 @@
#include <stdint.h>
#include <stddef.h>
#include <drivers/pci.h>
#include <port_io.h>
#include <stdio.h>
#define PCI_CONFIG_ADDRESS 0xCF8
#define PCI_CONFIG_DATA 0xCFC
uint32_t pci_config_read(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset)
{
uint32_t address = 0;
address |= (bus << 16); // Bus number (bits 23-16)
address |= (device << 11); // Device number (bits 15-11)
address |= (function << 8); // Function number (bits 10-8)
address |= (offset & 0xFC); // Register offset (bits 7-0)
address |= (1 << 31); // Enable configuration access
outl(PCI_CONFIG_ADDRESS, address); // Write address to PCI config space
return inl(PCI_CONFIG_DATA); // Read data from PCI config space
}
void pci_config_write(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value)
{
uint32_t address = 0x80000000 | (bus << 16) | (device << 11) | (function << 8) | (offset & 0xFC);
outl(PCI_CONFIG_ADDRESS, address); // Send the address to PCI config space
outl(PCI_CONFIG_DATA, value); // Write data to PCI config space
}
void pci_enumerate()
{
for (uint16_t bus = 0; bus < 256; bus++) // Maximum 256 buses
{
for (uint8_t device = 0; device < 32; device++) // Maximum 32 devices per bus
{
for (uint8_t function = 0; function < 8; function++) // Maximum 8 functions per device
{
uint32_t vendor_id = pci_config_read(bus, device, function, 0x00);
if (vendor_id == 0xFFFFFFFF) continue; // No device present
uint32_t device_id = pci_config_read(bus, device, function, 0x02);
uint32_t class_code = pci_config_read(bus, device, function, 0x08);
printf("Found PCI device: Vendor ID: 0x%X Device ID: 0x%X Class: 0x%X\n", vendor_id, device_id, class_code);
printf("0x%X", class_code);
printf(" --- ");
printf("0x%X\n", (class_code >> 8));
if ((class_code >> 8) == 0x0C) {
printf("Found USB controller (Class: 0x%02X)\n", class_code);
// Now, you can initialize and configure the USB controller.
}
}
}
}
}

0
drivers/pit.c Normal file
View File

98
drivers/ps2_keyboard.c Normal file
View File

@ -0,0 +1,98 @@
#include <stdio.h>
#include <port_io.h>
#include <drivers/ps2_keyboard.h>
/*
PS/2 Controller IO Ports
The PS/2 Controller itself uses 2 IO ports (IO ports 0x60 and 0x64). Like many IO ports, reads and writes may access different internal registers.
Historical note: The PC-XT PPI had used port 0x61 to reset the keyboard interrupt request signal (among other unrelated functions). Port 0x61 has no keyboard related functions on AT and PS/2 compatibles.
IO Port Access Type Purpose
0x60 Read/Write Data Port
0x64 Read Status Register
0x64 Write Command Register
*/
#define PS2_DATA_PORT 0x60
#define PS2_STATUS_PORT 0x64
#define KEYBOARD_IRQ 33
/*extern void register_interrupt_handler(int irq, void (*handler)(void));*/
static const char scancode_map[128] = {
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=','\b', /* Backspace */
'\t', /* Tab */
'q','w','e','r','t','y','u','i','o','p','[',']','\n', /* Enter */
0, /* Control */
'a','s','d','f','g','h','j','k','l',';','\'','`',
0, /* Left shift */
'\\','z','x','c','v','b','n','m',',','.','/',
0, /* Right shift */
'*',
0, /* Alt */
' ', /* Spacebar */
0, /* Caps lock */
/* The rest are function and control keys */
};
static bool shift_pressed = false;
void keyboard_handler(isr_stack_t* regs)
{
(void)regs; /* Only here to dismiss the "Unused parameter" warnings */
printd("PS/2 Keyboard handler fired!\n");
uint8_t scancode = inb(PS2_DATA_PORT);
/* Handle shift press/release */
if (scancode == 0x2A || scancode == 0x36)
{
shift_pressed = true;
return;
}
else if (scancode == 0xAA || scancode == 0xB6)
{
shift_pressed = false;
return;
}
if (scancode & 0x80)
{
/* Key release event, ignore for now */
}
else
{
char c = scancode_map[scancode];
if (shift_pressed && c >= 'a' && c <= 'z')
{
c -= 32; /* Convert to uppercase */
}
if (c)
{
/* Do something with the keypress (e.g., print or buffer) */
printf("Char: %c", c);
}
}
outb(0x20, 0x20); /* Acknowledge PIC */
}
void test_handler(isr_stack_t* r) {
printf("IRQ1 fired!\n");
}
void keyboard_init(void)
{
outb(0x21, inb(0x21) & ~0x02);
register_interrupt_handler(KEYBOARD_IRQ, test_handler);
/* Enable keyboard (controller-specific, often optional if already working) */
//outb(0x64, 0xAE);
}

177
drivers/tty.c Normal file
View File

@ -0,0 +1,177 @@
#include <types.h>
#include <string.h>
#include <port_io.h>
#include <vga/vga.h>
#include <tty.h>
static size_t terminal_row;
static size_t terminal_column;
static uint8_t terminal_color;
static uint16_t* terminal_buffer;
void terminal_initialize(void)
{
terminal_initializec(0x00);
}
void terminal_initializec(uint8_t color)
{
terminal_row = 0;
terminal_column = 0;
if (color == 0x00)
{
terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
}
else {
terminal_color = color;
}
terminal_buffer = VGA_MEMORY;
for (size_t y = 0; y < VGA_HEIGHT; y++)
{
for (size_t x = 0; x < VGA_WIDTH; x++)
{
const size_t index = y * VGA_WIDTH + x;
terminal_buffer[index] = vga_entry(' ', terminal_color);
}
}
}
void terminal_setcolor(uint8_t color)
{
terminal_color = color;
}
uint8_t terminal_getcolor(void)
{
return terminal_color;
}
void terminal_putentryat(unsigned char c, uint8_t color, size_t x, size_t y)
{
const size_t index = y * VGA_WIDTH + x;
terminal_buffer[index] = vga_entry(c, color);
}
void terminal_putchar(const char c)
{
unsigned char uc = c;
if (uc == '\n')
{
terminal_column = 0;
if (++terminal_row == VGA_HEIGHT)
{
terminal_scroll();
terminal_row = VGA_HEIGHT - 1;
}
return;
}
else if (uc == '\t')
{
terminal_column += 4;
if (terminal_column >= VGA_WIDTH)
{
terminal_putchar('\n');
}
return;
}
terminal_putentryat(uc, terminal_color, terminal_column, terminal_row);
if (++terminal_column == VGA_WIDTH)
{
terminal_column = 0;
if (++terminal_row == VGA_HEIGHT)
{
terminal_scroll();
terminal_row = VGA_HEIGHT - 1;
}
}
}
void terminal_write(const char* data, size_t size)
{
for (size_t i = 0; i < size; i++)
{
terminal_putchar(data[i]);
}
}
void terminal_writestring(const char* data)
{
terminal_write(data, strlen(data));
}
void terminal_debug_writestring(const char* data)
{
terminal_writestring("[ DEBUG ] ");
terminal_writestring(data);
}
void terminal_writeline(const char* data)
{
terminal_column = 0;
terminal_writestring(data);
}
void terminal_writechar_r(const char ch)
{
unsigned char uch = ch;
for (size_t i = 0; i < VGA_WIDTH; i++)
{
terminal_putchar(uch);
}
}
void terminal_clear(void)
{
terminal_clearl(-1);
}
void terminal_clearl(size_t num_lines)
{
if (num_lines == (size_t)-1)
{
terminal_initializec(terminal_getcolor());
}
else
{
/* XXX note to self: VGA_HEIGHT is 25, and VGA_WIDTH is 80 XXX */
/* static size_t terminal_row; static size_t terminal_column; */
terminal_row = VGA_HEIGHT;
terminal_column = 0;
while (terminal_row < num_lines)
{
terminal_writechar_r(' ');
terminal_row -= 1;
}
}
}
void terminal_scroll(void)
{
for (size_t y = 1; y < VGA_HEIGHT; y++)
{
for (size_t x = 0; x < VGA_WIDTH; x++)
{
const size_t src_index = y * VGA_WIDTH + x;
const size_t dst_index = (y - 1) * VGA_WIDTH + x;
terminal_buffer[dst_index] = terminal_buffer[src_index];
}
}
for (size_t x = 0; x < VGA_WIDTH; x++)
{
const size_t index = (VGA_HEIGHT - 1) * VGA_WIDTH + x;
terminal_buffer[index] = (terminal_color << 8) | ' ';
}
}

10
drivers/vga/vga.c Normal file
View File

@ -0,0 +1,10 @@
#include <stddef.h>
#include <vga/vga.h>
uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) {
return fg | bg << 4;
}
uint16_t vga_entry(unsigned char uc, uint8_t color) {
return (uint16_t) uc | (uint16_t) color << 8;
}

15
gdt.asm Normal file
View File

@ -0,0 +1,15 @@
global gdt_flush
gdt_flush:
mov eax, [esp + 4]
lgdt [eax]
; Reload segment registers
mov ax, 0x10 ; Kernel data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush ; Kernel code segment
.flush:
ret

3
grub.cfg Normal file
View File

@ -0,0 +1,3 @@
menuentry "Espresso" {
multiboot /boot/espresso.bin
}

40
idt.asm Normal file
View File

@ -0,0 +1,40 @@
[bits 32]
global idt_load
global common_isr_stub
extern isr_handler
extern _push_regs
extern _pop_regs
idt_load:
mov eax, [esp + 4]
lidt [eax]
ret
common_isr_stub:
call _push_regs ; Push all general-purpose registers
push ds
push es
push fs
push gs
mov ax, 0x10 ; Load kernel data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp ; Pass pointer to the register state
call isr_handler ; Call your C ISR handler
add esp, 4 ; Clean up stack from push esp
pop gs
pop fs
pop es
pop ds
call _pop_regs ; restore all general-purpose registers
add esp, 8 ; Remove int_no and err_code
sti
iret

6
include/drivers/ahci.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef _AHCI_H
#define _AHCI_H
#endif

40
include/drivers/ide.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef _IDE_H
#define _IDE_H
#include <stdint.h>
#include <stddef.h>
#define ATA_PRIMARY_IO 0x1F0
#define ATA_PRIMARY_CTRL 0x3F6
#define ATA_MASTER 0x00
#define ATA_SLAVE 0x10
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_SECCOUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_LBA3 0x06 // LBA48 higher bits
#define ATA_REG_LBA4 0x07 // LBA48 higher bits
#define ATA_REG_LBA5 0x08 // LBA48 higher bits
#define ATA_REG_HDDEVSEL 0x06
#define ATA_REG_COMMAND 0x07
#define ATA_REG_STATUS 0x07
#define ATA_CMD_READ_SECTORS 0x20
#define ATA_CMD_WRITE_SECTORS 0x30
#define ATA_CMD_READ_SECTORS_EXT 0x24
#define ATA_CMD_WRITE_SECTORS_EXT 0x34
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DRQ 0x08
#define ATA_SR_ERR 0x01
void ide_initialize(void);
int32_t ide_identify(uint8_t drive, uint16_t* buffer);
int32_t ide_read48(uint8_t drive, uint64_t lba, uint8_t sector_count, void* buffer);
int32_t ide_write48(uint8_t drive, uint64_t lba, uint8_t sector_count, const void* buffer);
#endif

10
include/drivers/pci.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _PCI_H
#define _PCI_H
#include <stdint.h>
uint32_t pci_config_read(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset);
void pci_config_write(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value);
void pci_enumerate(void);
#endif

7
include/drivers/pit.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef _PIT_H
#define _PIT_H
#include <types.h>
#endif

View File

@ -0,0 +1,10 @@
#ifndef _PS2_KEYBOARD_H
#define _PS2_KEYBOARD_H
#include <types.h>
#include <isr.h>
void keyboard_init(void);
void keyboard_handler(isr_stack_t* regs);
#endif

79
include/fs/duckfs.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef _DUCKFS_H
#define _DUCKFS_H
#include <stdint.h>
#define DFS_MAGIC 0xDF1984CC
#define DFS_BLOCK_SIZE 512
#define DFS_VERSION_0 "DuckFS, wheresDax?"
#define DFS_VERSION_1 "DuckFS, Terminator"
#define DFS_VERSION_2 "DuckFS-Terminator2"
#define DFS_VERSION_3 "DuckFS,StarWarsEIV"
#define DFS_VERSION_4 "DuckFS QUACK,QUACK"
#define DFS_FILE_ERROR -8
#define DFS_FILE_UNUSED -1
#define DFS_FILE_USED 0
#define DFS_FILE_TEXT 0
#define DFS_FILE_BINARY 1
#define DFS_FILE_DIRECTORY 2
/* encryption algorithms */
#define DFS_CAESAR_5 88
#define DFS_CAESAR_8 84
#define DFS_CAESAR_2 09
#define DFS_QUACK_32 99
#define DFS_MAX_FILENAME_LEN 64
#define DFS_MAX_FILES 256
typedef struct duckfs_file_header {
char filename[DFS_MAX_FILENAME_LEN + 1]; /* + 1 for the \0 */
char permissions[3]; /* Same thing here */
/*
512 Bytes per sector, meaning a 2 sector file is 1024 bytes, or 1 KiB.
Only valid if this file is not a directory.
*/
int32_t num_sectors;
int16_t type; /* Indicates the file type. -1 for null file, 0 for a text file, 1 for a binary file, and 2 for a directory. */
uint16_t _reserved0; /* (align to 4) */
struct duckfs_file_header* next;
struct duckfs_file_header* prev;
struct duckfs_file_header* contents; /* contains the directories files. only valid if this file is a directory. */
uint32_t lba_start; /* only is valid if this file is not a directory. */
uint32_t lba_end; /* only is valid if this file is not a directory. */
uint8_t _reserved1[158]; /* padding to 256 bytes total */
} duckfs_file_header_t;
typedef struct duckfs_superblock {
int32_t duckfs_magic; /* must be DFS_MAGIC. */
char duckfs_version_string[19];
char volume_label[19]; /* 18 characters and a null zero. */
int16_t encryption;
struct duckfs_file_header* duckfs_root;
uint32_t _padding[116];
} duckfs_superblock_t;
int32_t duckfs_init(int16_t drive);
void duckfs_format(int16_t drive);
int32_t duckfs_makedir(const char* filename, const char* perms);
void duckfs_free_directory(duckfs_file_header_t* dir);
duckfs_file_header_t duckfs_create_entry(const char* name, const char* perms, const char* type);
bool duckfs_add_entry(duckfs_file_header_t* dir, duckfs_file_header_t* entry);
#endif

16
include/fs/fat32.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _FAT32_H
#define _FAT32_H
#include <stdint.h>
#define FAT32_MAX_FILENAME 11
#define FAT32_ATTR_DIRECTORY 0x10
int32_t fat32_init(int32_t drive); /* 0 = primary master */
int32_t fat32_list_root(void);
int32_t fat32_read_file(const char *name, uint8_t *buffer, uint32_t max_len);
int32_t fat32_write_file(const char *name, const uint8_t *buffer, uint32_t len);
int32_t fat32_create_file(const char *name);
int32_t fat32_create_directory(const char *name);
int32_t fat32_change_directory(const char *name);
#endif

53
include/fs/ramfs.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef _RAM_FS_H
#define _RAM_FS_H
#include <stdint.h>
#include <stdbool.h>
#define RAMFS_MAX_FILES 128
#define RAMFS_BLOCK_SIZE 4096
#define RAMFS_MAX_PATH_LEN 255
#define RAMFS_PERM_READ 0x01
#define RAMFS_PERM_WRITE 0x02
#define RAMFS_PERM_EXEC 0x04
#define RAMFS_PERM_ALL (RAMFS_PERM_READ | RAMFS_PERM_WRITE | RAMFS_PERM_EXEC)
typedef struct ramfs_file {
char name[RAMFS_MAX_PATH_LEN];
uint32_t size;
uint8_t* data;
uint8_t permissions;
} ramfs_file_t;
typedef struct ramfs_directory {
char name[RAMFS_MAX_PATH_LEN];
ramfs_file_t* files[RAMFS_MAX_FILES];
uint32_t file_count;
} ramfs_directory_t;
void ramfs_init();
ramfs_file_t* ramfs_create_file(const char* name, uint32_t size, uint8_t permissions);
bool ramfs_delete_file(const char* name);
ramfs_file_t* ramfs_find_file(const char* name);
bool ramfs_create_directory(const char* name);
bool ramfs_delete_directory(const char* name);
bool ramfs_check_permissions(const char* name, uint8_t required_permissions);
bool ramfs_resize_file(const char* name, uint32_t new_size);
bool ramfs_append_to_file(const char* name, const uint8_t* data, uint32_t data_size);
bool ramfs_read_file(const char* name, uint8_t* buffer, uint32_t buffer_size);
bool ramfs_write_file(const char* name, const uint8_t* data, uint32_t size);
bool ramfs_overwrite_file(const char* name, const uint8_t* data, uint32_t size);
void ramfs_list_files();
void ramfs_list_directories();
#endif

23
include/gdt.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef _GDT_H
#define _GDT_H
#include <stdint.h>
struct gdt_entry {
uint16_t limit_low; /* Lower 16 bits of limit */
uint16_t base_low; /* Lower 16 bits of base */
uint8_t base_middle; /* Next 8 bits of base */
uint8_t access; /* Access flags */
uint8_t granularity; /* Granularity + high 4 bits of limit */
uint8_t base_high; /* Last 8 bits of base */
} __attribute__((packed));
struct gdt_ptr {
uint16_t limit; /* Size of GDT - 1 */
uint32_t base; /* Base address of GDT */
} __attribute__((packed));
void gdt_install();
void gdt_set_entry(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran);
#endif

25
include/idt.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef _IDT_H
#define _IDT_H
#include <stdint.h>
struct idt_entry {
uint16_t base_low;
uint16_t sel; /* Kernel segment selector */
uint8_t always0; /* Always 0 */
uint8_t flags; /* Type and attributes */
uint16_t base_high;
} __attribute__((packed));
struct idt_ptr {
uint16_t limit;
uint32_t base;
} __attribute__((packed));
void idt_install(void);
void idt_install_isrs(void);
void idt_install_syscall(void);
void pic_remap(void);
void idt_set_entry(int num, uint32_t base, uint16_t sel, uint8_t flags);
#endif

16
include/io.h Normal file
View File

@ -0,0 +1,16 @@
/* Defines a abstract base for I/O in the Espresso kernel. */
#ifndef ESPRESSO_IO_H
#define ESPRESSO_IO_H
#include <stdint.h>
#include <text_display.h>
void print(const char* str);
char* input(uint8_t dev=0);
void writefile(uint8_t mode, char* file, char* data);
char* readfile(char* file);
#endif

23
include/isr.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef _ISR_H
#define _ISR_H
#include <types.h>
typedef struct {
uint32_t ds, edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t int_no, err_code;
uint32_t eip, cs, eflags, useresp, ss;
} regs_t;
typedef struct {
uint32_t ds;
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t int_no, err_code;
uint32_t eip, cs, eflags, useresp, ss;
} isr_stack_t;
void isr_handler(isr_stack_t *r);
void syscall_handler(regs_t *r);
void register_interrupt_handler(uint8_t n, void (*handler)(isr_stack_t *));
#endif

7
include/kernel/boot.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef _KERNEL_BOOT_H
#define _KERNEL_BOOT_H
uint8_t parse_boot_data(const char* data);
#endif

View File

@ -0,0 +1,8 @@
#ifndef _INTRO_ANIMATION_H
#define _INTRO_ANIMATION_H
#include <types.h>
int16_t begin_anim(const char* version);
#endif

View File

@ -0,0 +1,8 @@
#ifndef _SYSCALLS_H
#define _SYSCALLS_H
#include <stdint.h>
int16_t syscall_write(int32_t fd, const void* buffer, int32_t length);
#endif

View File

@ -0,0 +1,18 @@
#ifndef _SOFTWARE_FLOAT_H
#define _SOFTWARE_FLOAT_H
#include <stdint.h>
typedef struct {
uint8_t sign; /* 0 = positive, 1 = negative */
uint8_t exponent; /* 8-bit biased exponent (bias = 127) */
uint32_t mantissa; /* 23 bits stored (MSB implicit for normalized numbers) */
} soft_float32_t;
typedef struct {
uint8_t sign; /* 0 = positive, 1 = negative */
uint16_t exponent; /* 11-bit biased exponent (bias = 1023) */
uint64_t mantissa; /* 52 bits stored (MSB implicit for normalized numbers) */
} soft_float64_t;
#endif

19
include/mm/heap.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _HEAP_H
#define _HEAP_H
#include <types.h>
#define HEAP_START 0xC0000000
#define HEAP_SIZE (1024 * 4096) /* 1MB heap */
void heap_init(void);
void* malloc(size_t size);
void* malloc_aligned(size_t size, size_t alignment);
void* calloc(size_t nmemb, size_t size);
void* realloc(void* ptr, size_t size);
void free(void* ptr);
#endif

8
include/mm/mm.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef _MM_H
#define _MM_H
#include <mm/pmm.h>
#include <mm/heap.h>
#include <mm/paging.h>
#endif

9
include/mm/paging.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _PAGING_H
#define _PAGING_H
#include <types.h>
void paging_init(void);
void map_page(void* phys, void* virt);
#endif

11
include/mm/pmm.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _PMM_H
#define _PMM_H
#include <stdint.h>
#include <multiboot.h>
void pmm_init(multiboot_info_t* mb_info);
void* alloc_page(void);
void free_page(void* ptr);
#endif

274
include/multiboot.h Normal file
View File

@ -0,0 +1,274 @@
/* multiboot.h - Multiboot header file. */
/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef MULTIBOOT_HEADER
#define MULTIBOOT_HEADER 1
/* How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 8192
#define MULTIBOOT_HEADER_ALIGN 4
/* The magic field should contain this. */
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
/* This should be in %eax. */
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
/* Alignment of multiboot modules. */
#define MULTIBOOT_MOD_ALIGN 0x00001000
/* Alignment of the multiboot info structure. */
#define MULTIBOOT_INFO_ALIGN 0x00000004
/* Flags set in the flags member of the multiboot header. */
/* Align all boot modules on i386 page (4KB) boundaries. */
#define MULTIBOOT_PAGE_ALIGN 0x00000001
/* Must pass memory information to OS. */
#define MULTIBOOT_MEMORY_INFO 0x00000002
/* Must pass video information to OS. */
#define MULTIBOOT_VIDEO_MODE 0x00000004
/* This flag indicates the use of the address fields in the header. */
#define MULTIBOOT_AOUT_KLUDGE 0x00010000
/* Flags to be set in the flags member of the multiboot info structure. */
/* is there basic lower/upper memory information? */
#define MULTIBOOT_INFO_MEMORY 0x00000001
/* is there a boot device set? */
#define MULTIBOOT_INFO_BOOTDEV 0x00000002
/* is the command-line defined? */
#define MULTIBOOT_INFO_CMDLINE 0x00000004
/* are there modules to do something with? */
#define MULTIBOOT_INFO_MODS 0x00000008
/* These next two are mutually exclusive */
/* is there a symbol table loaded? */
#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
/* is there an ELF section header table? */
#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
/* is there a full memory map? */
#define MULTIBOOT_INFO_MEM_MAP 0x00000040
/* Is there drive info? */
#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
/* Is there a config table? */
#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
/* Is there a boot loader name? */
#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
/* Is there a APM table? */
#define MULTIBOOT_INFO_APM_TABLE 0x00000400
/* Is there video information? */
#define MULTIBOOT_INFO_VBE_INFO 0x00000800
#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000
#ifndef ASM_FILE
typedef unsigned char multiboot_uint8_t;
typedef unsigned short multiboot_uint16_t;
typedef unsigned int multiboot_uint32_t;
typedef unsigned long long multiboot_uint64_t;
struct multiboot_header
{
/* Must be MULTIBOOT_MAGIC - see above. */
multiboot_uint32_t magic;
/* Feature flags. */
multiboot_uint32_t flags;
/* The above fields plus this one must equal 0 mod 2^32. */
multiboot_uint32_t checksum;
/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
multiboot_uint32_t header_addr;
multiboot_uint32_t load_addr;
multiboot_uint32_t load_end_addr;
multiboot_uint32_t bss_end_addr;
multiboot_uint32_t entry_addr;
/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
multiboot_uint32_t mode_type;
multiboot_uint32_t width;
multiboot_uint32_t height;
multiboot_uint32_t depth;
};
/* The symbol table for a.out. */
struct multiboot_aout_symbol_table
{
multiboot_uint32_t tabsize;
multiboot_uint32_t strsize;
multiboot_uint32_t addr;
multiboot_uint32_t reserved;
};
typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
/* The section header table for ELF. */
struct multiboot_elf_section_header_table
{
multiboot_uint32_t num;
multiboot_uint32_t size;
multiboot_uint32_t addr;
multiboot_uint32_t shndx;
};
typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;
struct multiboot_info
{
/* Multiboot info version number */
multiboot_uint32_t flags;
/* Available memory from BIOS */
multiboot_uint32_t mem_lower;
multiboot_uint32_t mem_upper;
/* "root" partition */
multiboot_uint32_t boot_device;
/* Kernel command line */
multiboot_uint32_t cmdline;
/* Boot-Module list */
multiboot_uint32_t mods_count;
multiboot_uint32_t mods_addr;
union
{
multiboot_aout_symbol_table_t aout_sym;
multiboot_elf_section_header_table_t elf_sec;
} u;
/* Memory Mapping buffer */
multiboot_uint32_t mmap_length;
multiboot_uint32_t mmap_addr;
/* Drive Info buffer */
multiboot_uint32_t drives_length;
multiboot_uint32_t drives_addr;
/* ROM configuration table */
multiboot_uint32_t config_table;
/* Boot Loader Name */
multiboot_uint32_t boot_loader_name;
/* APM table */
multiboot_uint32_t apm_table;
/* Video */
multiboot_uint32_t vbe_control_info;
multiboot_uint32_t vbe_mode_info;
multiboot_uint16_t vbe_mode;
multiboot_uint16_t vbe_interface_seg;
multiboot_uint16_t vbe_interface_off;
multiboot_uint16_t vbe_interface_len;
multiboot_uint64_t framebuffer_addr;
multiboot_uint32_t framebuffer_pitch;
multiboot_uint32_t framebuffer_width;
multiboot_uint32_t framebuffer_height;
multiboot_uint8_t framebuffer_bpp;
#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1
#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2
multiboot_uint8_t framebuffer_type;
union
{
struct
{
multiboot_uint32_t framebuffer_palette_addr;
multiboot_uint16_t framebuffer_palette_num_colors;
};
struct
{
multiboot_uint8_t framebuffer_red_field_position;
multiboot_uint8_t framebuffer_red_mask_size;
multiboot_uint8_t framebuffer_green_field_position;
multiboot_uint8_t framebuffer_green_mask_size;
multiboot_uint8_t framebuffer_blue_field_position;
multiboot_uint8_t framebuffer_blue_mask_size;
};
};
};
typedef struct multiboot_info multiboot_info_t;
struct multiboot_color
{
multiboot_uint8_t red;
multiboot_uint8_t green;
multiboot_uint8_t blue;
};
struct multiboot_mmap_entry
{
multiboot_uint32_t size;
multiboot_uint64_t addr;
multiboot_uint64_t len;
#define MULTIBOOT_MEMORY_AVAILABLE 1
#define MULTIBOOT_MEMORY_RESERVED 2
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
#define MULTIBOOT_MEMORY_NVS 4
#define MULTIBOOT_MEMORY_BADRAM 5
multiboot_uint32_t type;
} __attribute__((packed));
typedef struct multiboot_mmap_entry multiboot_memory_map_t;
struct multiboot_mod_list
{
/* the memory used goes from bytes mod_start to mod_end-1 inclusive */
multiboot_uint32_t mod_start;
multiboot_uint32_t mod_end;
/* Module command line */
multiboot_uint32_t cmdline;
/* padding to take it to 16 bytes (must be zero) */
multiboot_uint32_t pad;
};
typedef struct multiboot_mod_list multiboot_module_t;
/* APM BIOS info. */
struct multiboot_apm_info
{
multiboot_uint16_t version;
multiboot_uint16_t cseg;
multiboot_uint32_t offset;
multiboot_uint16_t cseg_16;
multiboot_uint16_t dseg;
multiboot_uint16_t flags;
multiboot_uint16_t cseg_len;
multiboot_uint16_t cseg_16_len;
multiboot_uint16_t dseg_len;
};
#endif /* ! ASM_FILE */
#endif /* ! MULTIBOOT_HEADER */

30
include/panic.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef _KERNEL_PANIC_H
#define _KERNEL_PANIC_H
#include <stddef.h>
#include <tty.h>
void panic(const char* arr)
{
uint8_t color = 0x1F;
uint8_t blank = 0xFF;
for (size_t y = 0; y < VGA_HEIGHT; y++)
{
for (size_t x = 0; x < VGA_WIDTH; x++)
{
const size_t index = y * VGA_WIDTH + x;
VGA_MEMORY[index] = (uint16_t) ' ' | (uint16_t) blank << 8;
}
}
int i;
for (i = 0; i < 75; i += 2)
{
VGA_MEMORY[i] = arr[i];
VGA_MEMORY[i + 1] = color;
}
}
#endif

46
include/port_io.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef _PORT_IO_H
#define _PORT_IO_H
#include <stdint.h>
static inline uint32_t inl(uint16_t port)
{
uint32_t rv;
asm volatile ("inl %%dx, %%eax" : "=a" (rv) : "dN" (port));
return rv;
}
static inline void outl(uint16_t port, uint32_t data)
{
asm volatile ("outl %%eax, %%dx" : : "dN" (port), "a" (data));
}
static inline void outb(uint16_t port, uint8_t val)
{
asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) : "memory");
}
static inline uint8_t inb(uint16_t port)
{
uint8_t ret;
asm volatile ( "inb %1, %0"
: "=a"(ret)
: "Nd"(port)
: "memory");
return ret;
}
static inline void io_wait()
{
outb(0x80, 0);
}
static inline void insw(uint16_t port, void* addr, uint32_t count) {
__asm__ volatile ("rep insw" : "+D"(addr), "+c"(count) : "d"(port) : "memory");
}
static inline void outsw(uint16_t port, const void* addr, uint32_t count) {
__asm__ volatile ("rep outsw" : "+S"(addr), "+c"(count) : "d"(port));
}
#endif

19
include/printf.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _PRINTF_H
#define _PRINTF_H
#include <tty.h>
#include <string.h>
#include <vga/vga.h> /* Only for the vga_color enum */
void printwc(const char*, uint8_t);
void printd(const char* str);
void printf(const char*, ...);
void print_int(int32_t value);
void print_uint(uint32_t value);
void print_hex(uint32_t value, int width, bool uppercase);
void print_double(double value, int precision);
#endif

19
include/processes.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _PROCESSES_H
#define _PROCESSES_H
#include <stdint.h>
struct process
{
uint16_t id;
uint8_t policy;
uint16_t priority;
char name[16];
struct process* next;
};
#endif

2
include/stdio.h Normal file
View File

@ -0,0 +1,2 @@
#include <printf.h>

9
include/stdlib.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef STDLIB_H
#define STDLIB_H
#include <mm/mm.h>
#include <stdio.h>
#include <string.h>
#include <types.h>
#endif

27
include/string.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef STRING_H
#define STRING_H
#include <types.h>
/* TODO: change all applicable 'int32_t's with 'size_t's. */
size_t strlen(const char* str);
size_t strcmp(const char *s1, const char *s2);
int32_t strncmp(const char *s1, const char *s2, size_t n);
size_t strcpy(char *dst, const char *src);
char *strncpy(char *dest, const char *src, uint32_t n);
void strcat(char *dest, const char *src);
int32_t ischar(int32_t c);
int32_t isspace(char c);
int32_t isalpha(char c);
char upper(char c);
char toupper(char c);
char lower(char c);
void *memset(void *dst, char c, uint32_t n);
void *memcpy(void *dst, const void *src, uint32_t n);
int32_t memcmp(uint8_t *s1, uint8_t *s2, uint32_t n);
#endif

6
include/syscall.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef _SYSCALL_H
#define _SYSCALL_H
#endif

28
include/tty.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _TTY_H
#define _TTY_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;
static uint16_t* const VGA_MEMORY = (uint16_t*) 0xB8000;
void terminal_initialize(void);
void terminal_initializec(uint8_t color);
void terminal_putentryat(unsigned char c, uint8_t color, size_t x, size_t y);
void terminal_putchar(char c);
void terminal_write(const char* data, size_t size);
void terminal_writestring(const char* data);
void terminal_debug_writestring(const char* data);
void terminal_setcolor(uint8_t color);
uint8_t terminal_getcolor(void);
void terminal_clear(void);
void terminal_clearl(size_t num_lines);
void terminal_scroll(void);
#endif

8
include/types.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef _TYPES_H
#define _TYPES_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#endif

View File

@ -0,0 +1,27 @@
#ifndef _SSE_H
#define _SSE_H
#define SSE_XMM_SIZE 128
void enable_sse(void);
int32_t test_sse(void);
void sse2_add_double_arrays(double *dst, const double *a, const double *b, size_t count);
void sse2_add_int32_arrays(int32_t *dst, const int32_t *a, const int32_t *b, size_t count);
void sse2_add_int64_arrays(int64_t *dst, const int64_t *a, const int64_t *b, size_t count);
char *sse2_strncpy(char *dest, const char *src, uint32_t n);
void *sse2_memcpy(void *dst, const void *src, uint32_t n);
void double_vector_to_int_vector(const double *src, int32_t *dst);
void int_vector_to_double_vector(const int32_t *src, double *dst);
void *memclr_sse2(const void *const m_start, const size_t m_count);
#endif

30
include/vga/vga.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef VGA_DRIVER_H
#define VGA_DRIVER_H
#include <stdint.h>
/* Hardware text mode color constants. */
enum vga_color {
VGA_COLOR_BLACK = 0,
VGA_COLOR_BLUE = 1,
VGA_COLOR_GREEN = 2,
VGA_COLOR_CYAN = 3,
VGA_COLOR_RED = 4,
VGA_COLOR_MAGENTA = 5,
VGA_COLOR_BROWN = 6,
VGA_COLOR_LIGHT_GREY = 7,
VGA_COLOR_DARK_GREY = 8,
VGA_COLOR_LIGHT_BLUE = 9,
VGA_COLOR_LIGHT_GREEN = 10,
VGA_COLOR_LIGHT_CYAN = 11,
VGA_COLOR_LIGHT_RED = 12,
VGA_COLOR_LIGHT_MAGENTA = 13,
VGA_COLOR_LIGHT_BROWN = 14,
VGA_COLOR_WHITE = 15,
};
uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg);
uint16_t vga_entry(unsigned char uc, uint8_t color);
#endif

34
isr128.asm Normal file
View File

@ -0,0 +1,34 @@
[bits 32]
global isr128
extern isr_handler
extern _push_regs
extern _pop_regs
isr128:
cli
call _push_regs
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Pass register state to C handler
push esp
call isr_handler
add esp, 4
pop gs
pop fs
pop es
pop ds
call _pop_regs
sti
iret

87
isr_stub_table.asm Normal file
View File

@ -0,0 +1,87 @@
[bits 32]
global isr_stub_table
global isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7
global isr8, isr9, isr10, isr11, isr12, isr13, isr14, isr15
global isr16, isr17, isr18, isr19, isr20, isr21, isr22, isr23
global isr24, isr25, isr26, isr27, isr28, isr29, isr30, isr31
global isr33
extern isr_handler
extern _push_regs
extern _pop_regs
%macro ISR_NOERR 1
isr%1:
cli
push dword 0 ; Fake error code
push dword %1 ; Interrupt number
jmp isr_common_stub
%endmacro
%macro ISR_ERR 1
isr%1:
cli
push dword %1 ; Interrupt number
jmp isr_common_stub
%endmacro
; ISRs 0-31 (exceptions)
ISR_NOERR 0
ISR_NOERR 1
ISR_NOERR 2
ISR_NOERR 3
ISR_NOERR 4
ISR_NOERR 5
ISR_NOERR 6
ISR_NOERR 7
ISR_ERR 8 ; Double fault
ISR_NOERR 9
ISR_ERR 10 ; Invalid TSS
ISR_ERR 11 ; Segment not present
ISR_ERR 12 ; Stack segment fault
ISR_ERR 13 ; General protection fault
ISR_ERR 14 ; Page fault
ISR_NOERR 15
ISR_NOERR 16
ISR_NOERR 17
ISR_NOERR 18
ISR_NOERR 19
ISR_NOERR 20
ISR_NOERR 21
ISR_NOERR 22
ISR_NOERR 23
ISR_NOERR 24
ISR_NOERR 25
ISR_NOERR 26
ISR_NOERR 27
ISR_NOERR 28
ISR_NOERR 29
ISR_ERR 30 ; Security exception
ISR_NOERR 31
ISR_NOERR 33
isr_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp
call isr_handler
add esp, 4
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
;sti
iret

15
kernel/boot.c Normal file
View File

@ -0,0 +1,15 @@
#include <stdint.h>
#include <string.h>
#include <kernel/boot.h>
uint8_t parse_boot_data(const char* data)
{
if (strlen(data) == 0)
{
return 0;
}
return 0;
}

72
kernel/intro_anim.c Normal file
View File

@ -0,0 +1,72 @@
#include <string.h>
#include <tty.h>
#include <stdio.h>
#include <port_io.h>
#include <kernel/intro_anim.h>
void delay_us(uint32_t microseconds, uint32_t cpu_mhz) {
uint32_t count = cpu_mhz * microseconds;
while (count--) {
asm volatile ("nop" ::: "memory");
}
}
int16_t begin_anim(const char* version)
{
if (strlen(version) < 5)
{
return -1;
}
const char* e = "Espresso";
const char* v = version;
const char* n = "Press any key to continue";
int32_t pos = 0;
int32_t v_pos = 0;
int16_t b = 0;
int32_t sh_pos = VGA_WIDTH / 2;
int32_t sv_pos = VGA_HEIGHT / 2;
while (true)
{
terminal_clear();
for (int32_t i = 0; n[i]; ++i)
{
terminal_putentryat(n[i], terminal_getcolor(), sh_pos, sv_pos);
sh_pos++;
}
for (int32_t i = 0; e[i]; ++i)
{
terminal_putentryat(e[i], terminal_getcolor(), pos + i, v_pos);
}
if ((v_pos + 1) == VGA_HEIGHT)
{
v_pos = 0;
b = 1;
}
for (int32_t i = 0; v[i]; ++i)
{
terminal_putentryat(v[i], terminal_getcolor(), pos + i + 3, (b == 0 ? (v_pos + 1) : v_pos));
}
pos += 2;
v_pos++;
sh_pos = VGA_WIDTH / 2;
b = 0;
delay_us(50000, 5000);
}
terminal_clear();
return 0;
}

137
kernel/kernel.c Normal file
View File

@ -0,0 +1,137 @@
/* Check if the compiler thinks you are targeting the wrong operating system. */
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif
/* This tutorial will only work for the 32-bit ix86 targets. */
#if !defined(__i386__)
#error "This kernel needs to be compiled with a ix86-elf compiler"
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <kernel/boot.h>
#include <panic.h>
#include <gdt.h>
#include <idt.h>
#include <multiboot.h>
#include <stdio.h>
#include <drivers/pci.h>
#include <drivers/ps2_keyboard.h>
//#include <drivers/ahci.h>
#include <drivers/ide.h>
#include <mm/mm.h>
#include <fs/ramfs.h>
#include <fs/fat32.h>
#include <fs/duckfs.h>
#include <vector_extentions/sse.h>
#include <kernel/intro_anim.h>
#define DEBUG
/*extern HBA_MEM *abar;
extern HBA_PORT *ahci_port;*/
extern void _hang_asm(void);
void kernel_main(multiboot_info_t* mbd, unsigned int magic)
{
/* --- BEGIN INITIALIZATION SECTION --- */
/* We need to initialize the terminal so that any error/debuging messages show. */
terminal_initialize();
terminal_setcolor(VGA_COLOR_RED);
/* Make sure the magic number matches for memory mapping*/
if(magic != MULTIBOOT_BOOTLOADER_MAGIC)
{
printf("[ ERROR ] invalid magic number!\n");
_hang_asm();
}
/* Check bit 6 to see if we have a valid memory map */
if(!(mbd->flags >> 6 & 0x1))
{
printf("[ ERROR ] invalid memory map given by GRUB bootloader\n");
_hang_asm();
}
gdt_install();
pic_remap();
idt_install_isrs();
idt_install();
terminal_setcolor(VGA_COLOR_GREEN);
printd("Initializing physical memory manager...\n");
pmm_init(mbd);
printd("Physical memory manager initialized\n");
printd("Initializing paging...\n");
paging_init();
printd("Paging initialized\n");
printd("Initializing heap allocator...\n");
heap_init();
printd("Heap allocator initialized\n");
printd("Testing SSE...\n");
int32_t sse_test_result = test_sse();
if (sse_test_result != 0)
{
printf("[ DEBUG ] SSE test failed with RV %d\n", sse_test_result);
}
else
{
printd("SSE test succeeded\n");
}
/*
printd("Initalizing AHCI...\n");
ahci_init();
printd("AHCI initialized\n");
*/
/*printd("Initializing RAMFS...\n");
ramfs_init();
printd("RAMFS initialized.\n");*/
printd("Initializing IDE system...\n");
ide_initialize();
printd("IDE initialized\n");
printd("Initializing DuckFS...\n");
int32_t duckfs_init_rv = duckfs_init(0);
printf("[ DEBUG ] DuckFS initialized with RV %d\n", duckfs_init_rv);
/* --- END INITIALIZATION SECTION --- */
terminal_setcolor(VGA_COLOR_LIGHT_GREEN);
const char* espresso_kernel_version = "0.0.0b";
printf("Loading Espresso %s...\n", espresso_kernel_version);
/*begin_anim(espresso_kernel_version);*/
printf("Hello and welcome to Espresso\n");
_hang_asm();
}

10
kernel/syscalls.c Normal file
View File

@ -0,0 +1,10 @@
#include <stdio.h>
#include <kernel/syscalls.h>
int16_t syscall_write(int32_t fd, const void* buffer, int32_t length)
{
return -1;
}

190
lib/fs/ramfs/ramfs.c Normal file
View File

@ -0,0 +1,190 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fs/ramfs.h>
static ramfs_file_t ramfs_files[RAMFS_MAX_FILES];
static ramfs_directory_t root_directory = { .name = "/", .file_count = 0 };
void ramfs_init()
{
memset(ramfs_files, 0, sizeof(ramfs_files));
root_directory.file_count = 0;
}
ramfs_file_t* ramfs_create_file(const char* name, uint32_t size, uint8_t permissions)
{
for (uint32_t i = 0; i < RAMFS_MAX_FILES; i++) {
if (ramfs_files[i].name[0] == '\0') {
// Create the file
ramfs_file_t* file = &ramfs_files[i];
strncpy(file->name, name, RAMFS_MAX_PATH_LEN);
file->size = size;
file->data = (size > 0) ? malloc(size) : NULL;
file->permissions = permissions;
if (root_directory.file_count < RAMFS_MAX_FILES) {
root_directory.files[root_directory.file_count++] = file;
}
return file;
}
}
return NULL;
}
bool ramfs_delete_file(const char* name)
{
for (uint32_t i = 0; i < root_directory.file_count; i++) {
if (strcmp(root_directory.files[i]->name, name) == 0) {
free(root_directory.files[i]->data);
root_directory.files[i] = root_directory.files[root_directory.file_count - 1];
root_directory.file_count--;
return true;
}
}
return false;
}
ramfs_file_t* ramfs_find_file(const char* name)
{
for (uint32_t i = 0; i < root_directory.file_count; i++) {
if (strcmp(root_directory.files[i]->name, name) == 0) {
return root_directory.files[i];
}
}
return NULL;
}
bool ramfs_resize_file(const char* name, uint32_t new_size)
{
ramfs_file_t* file = ramfs_find_file(name);
if (file)
{
file->data = realloc(file->data, new_size);
file->size = new_size;
return true;
}
return false;
}
bool ramfs_append_to_file(const char* name, const uint8_t* data, uint32_t data_size)
{
ramfs_file_t* file = ramfs_find_file(name);
if (file && (file->permissions & RAMFS_PERM_WRITE))
{
file->data = realloc(file->data, file->size + data_size + 1);
if (!file->data) return false;
memcpy(file->data + file->size, data, data_size);
file->size += data_size;
file->data[file->size] = '\0';
return true;
}
return false;
}
bool ramfs_create_directory(const char* name)
{
if (root_directory.file_count < RAMFS_MAX_FILES) {
ramfs_directory_t* dir = (ramfs_directory_t*)malloc(sizeof(ramfs_directory_t));
strncpy(dir->name, name, RAMFS_MAX_PATH_LEN);
dir->file_count = 0;
root_directory.files[root_directory.file_count++] = (ramfs_file_t*)dir;
return true;
}
return false;
}
bool ramfs_delete_directory(const char* name)
{
for (uint32_t i = 0; i < root_directory.file_count; i++) {
if (strcmp(root_directory.files[i]->name, name) == 0) {
// Delete the directory (along with its files)
ramfs_directory_t* dir = (ramfs_directory_t*)root_directory.files[i];
for (uint32_t j = 0; j < dir->file_count; j++) {
free(dir->files[j]->data);
}
free(dir);
root_directory.files[i] = root_directory.files[root_directory.file_count - 1];
root_directory.file_count--;
return true;
}
}
return false;
}
bool ramfs_check_permissions(const char* name, uint8_t required_permissions)
{
ramfs_file_t* file = ramfs_find_file(name);
if (file) {
return (file->permissions & required_permissions) == required_permissions;
}
return false;
}
void ramfs_list_files()
{
printf("Files in RAMFS:\n");
for (uint32_t i = 0; i < root_directory.file_count; i++) {
printf("%s (Size: %u bytes)\n", root_directory.files[i]->name, root_directory.files[i]->size);
}
}
void ramfs_list_directories()
{
printf("Directories in RAMFS:\n");
for (uint32_t i = 0; i < root_directory.file_count; i++) {
if (root_directory.files[i]->data != NULL) {
printf("%s\n", root_directory.files[i]->name);
}
}
}
bool ramfs_read_file(const char* name, uint8_t* buffer, uint32_t buffer_size)
{
ramfs_file_t* file = ramfs_find_file(name);
if (file && (file->permissions & RAMFS_PERM_READ))
{
uint32_t bytes_to_read = (file->size < buffer_size) ? file->size : buffer_size;
memcpy(buffer, file->data, bytes_to_read);
return true;
}
return false;
}
bool ramfs_write_file(const char* name, const uint8_t* data, uint32_t size)
{
return ramfs_append_to_file(name, data, size);
}
bool ramfs_overwrite_file(const char* name, const uint8_t* data, uint32_t size)
{
ramfs_file_t* file = ramfs_find_file(name);
if (!file || !(file->permissions & RAMFS_PERM_WRITE)) {
return false;
}
// Allocate space for data + null terminator
uint8_t* new_data = realloc(file->data, size + 1);
if (!new_data && size > 0) {
return false; // realloc failed
}
file->data = new_data;
if (size > 0 && data) {
memcpy(file->data, data, size);
}
file->data[size] = '\0'; // Ensure null-terminated
file->size = size;
return true;
}

26
lib/math/software_float.c Normal file
View File

@ -0,0 +1,26 @@
#include <math/software_float.h>
/* A float = FLOAT_SIGN * FLOAT_MANTISSA * (2 ^ FLOAT_EXPONENT) */
soft_float32_t decode_float(uint32_t raw)
{
soft_float32_t f;
f.sign = (raw >> 31) & 1;
f.exponent = (raw >> 23) & 0xFF;
f.mantissa = raw & 0x7FFFFF;
return f;
}
soft_float64_t decode_double(uint64_t raw)
{
soft_float64_t d;
d.sign = (raw >> 63) & 1;
d.exponent = (raw >> 52) & 0x7FF;
d.mantissa = raw & 0xFFFFFFFFFFFFF;
return d;
}

136
lib/mm/heap.c Normal file
View File

@ -0,0 +1,136 @@
#include <stdio.h>
#include <string.h>
#include <mm/heap.h>
#include <mm/pmm.h>
#include <mm/paging.h>
#define ALIGNMENT 8
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1))
typedef struct block_header {
size_t size;
struct block_header* next;
int free;
} block_header_t;
#define BLOCK_SIZE sizeof(block_header_t)
static uint8_t* heap_base = (uint8_t*)HEAP_START;
static uint8_t* heap_end = (uint8_t*)(HEAP_START + HEAP_SIZE);
static block_header_t* free_list = NULL;
void heap_init(void)
{
free_list = (block_header_t*)heap_base;
free_list->size = HEAP_SIZE - BLOCK_SIZE;
free_list->next = NULL;
free_list->free = 1;
}
void* malloc(size_t size)
{
size = ALIGN(size);
block_header_t* curr = free_list;
while (curr)
{
if (curr->free && curr->size >= size)
{
/* Split if there's space for another block */
if (curr->size >= size + BLOCK_SIZE + ALIGNMENT)
{
block_header_t* new_block = (block_header_t*)((uint8_t*)curr + BLOCK_SIZE + size);
new_block->size = curr->size - size - BLOCK_SIZE;
new_block->next = curr->next;
new_block->free = 1;
curr->next = new_block;
curr->size = size;
}
curr->free = 0;
return (void*)((uint8_t*)curr + BLOCK_SIZE);
}
curr = curr->next;
}
printd("Malloc failed due to lack of free memory\n");
return NULL;
}
void* calloc(size_t nmemb, size_t size)
{
size_t total = nmemb * size;
void* ptr = malloc(total);
if (ptr)
{
memset(ptr, 0, total);
}
return ptr;
}
void* realloc(void* ptr, size_t size)
{
if (!ptr)
{
return malloc(size);
}
if (size == 0)
{
free(ptr);
return NULL;
}
block_header_t* block = (block_header_t*)((uint8_t*)ptr - BLOCK_SIZE);
if (block->size >= size)
{
return ptr;
}
void* new_ptr = malloc(size);
if (new_ptr) {
memcpy(new_ptr, ptr, block->size);
free(ptr);
}
return new_ptr;
}
void free(void* ptr)
{
if (!ptr)
{
return;
}
block_header_t* block = (block_header_t*)((uint8_t*)ptr - BLOCK_SIZE);
block->free = 1;
/* Forward coalescing */
if (block->next && block->next->free)
{
block->size += BLOCK_SIZE + block->next->size;
block->next = block->next->next;
}
/* Backward coalescing */
block_header_t* prev = NULL;
block_header_t* curr = free_list;
while (curr && curr != block)
{
prev = curr;
curr = curr->next;
}
if (prev && prev->free)
{
prev->size += BLOCK_SIZE + block->size;
prev->next = block->next;
}
}

82
lib/mm/paging.c Normal file
View File

@ -0,0 +1,82 @@
#include <string.h>
#include <stdio.h>
#include <mm/paging.h>
#include <mm/pmm.h>
#define PAGE_DIRECTORY_ENTRIES 1024
#define PAGE_TABLE_ENTRIES 1024
#define PAGE_SIZE 4096
typedef uint32_t page_directory_entry_t;
typedef uint32_t page_table_entry_t;
static page_directory_entry_t* page_directory = (page_directory_entry_t*)0x9C000; /* Must be page-aligned */
static page_table_entry_t* page_tables[PAGE_DIRECTORY_ENTRIES];
extern void _enable_paging_asm(void);
void paging_init(void)
{
/* Allocate and clear the page directory */
page_directory = (uint32_t*)alloc_page();
memset(page_directory, 0, 4096);
/* Allocate and set up the first identity-mapped page table (04MB) */
uint32_t* first_table = (uint32_t*)alloc_page();
memset(first_table, 0, 4096);
for (uint32_t i = 0; i < 1024; i++)
{
first_table[i] = (i * 0x1000) | 3; /* Present | RW */
}
page_directory[0] = ((uint32_t)first_table) | 3;
/* --- Map 4MB heap at 0xC0000000 --- */
uint32_t* heap_table = (uint32_t*)alloc_page();
memset(heap_table, 0, 4096);
for (uint32_t i = 0; i < 1024; i++) /* 256 pages = 1MB */
{
uint32_t phys = (uint32_t)alloc_page();
if (phys == 0)
{
printf("Out of physical memory during heap mapping!\n");
while (1);
}
heap_table[i] = phys | 3; /* Present | RW */
}
/* 0xC0000000 >> 22 == 768 */
page_directory[768] = ((uint32_t)heap_table) | 3;
// Load page directory
asm volatile ("mov %0, %%cr3" : : "r"(page_directory));
/* Enable paging */
_enable_paging_asm();
}
void map_page(void* phys_addr, void* virt_addr)
{
uint32_t pd_index = ((uint32_t)virt_addr >> 22) & 0x3FF;
uint32_t pt_index = ((uint32_t)virt_addr >> 12) & 0x3FF;
/* Allocate page table if necessary */
if (!(page_directory[pd_index] & 1))
{
void* pt_phys = alloc_page();
page_tables[pd_index] = (page_table_entry_t*)((uint32_t)pt_phys + 0xC0000000); /* Map it higher */
for (int i = 0; i < PAGE_TABLE_ENTRIES; i++)
{
page_tables[pd_index][i] = 0x00000002; /* Not present */
}
page_directory[pd_index] = ((uint32_t)pt_phys) | 0x3; /* Present, R/W */
}
page_table_entry_t* page_table = (page_table_entry_t*)((page_directory[pd_index] & 0xFFFFF000) | 0xC0000000);
page_table[pt_index] = ((uint32_t)phys_addr & 0xFFFFF000) | 0x3; /* Present, R/W */
asm volatile ("invlpg (%0)" :: "r" (virt_addr) : "memory");
}

79
lib/mm/pmm.c Normal file
View File

@ -0,0 +1,79 @@
#include <stddef.h>
#include <mm/pmm.h>
#define PAGE_SIZE 4096
#define BITMAP_SIZE (1024 * 1024) /* Supports up to 4GB RAM (1 bit per page) */
static uint8_t bitmap[BITMAP_SIZE / 8];
static uint32_t total_pages;
static uint32_t used_pages = 0;
static inline void set_bit(uint32_t idx)
{
bitmap[idx / 8] |= (1 << (idx % 8));
}
static inline void clear_bit(uint32_t idx)
{
bitmap[idx / 8] &= ~(1 << (idx % 8));
}
static inline int test_bit(uint32_t idx)
{
return (bitmap[idx / 8] >> (idx % 8)) & 1;
}
void pmm_init(multiboot_info_t* mb_info)
{
total_pages = 0x100000; /* 4GB / 4KB = 1M pages */
for (uint32_t i = 0; i < total_pages / 8; i++)
{
bitmap[i] = 0xFF; /* Mark all as used */
}
multiboot_memory_map_t* mmap = (multiboot_memory_map_t*) mb_info->mmap_addr;
while ((uint32_t)mmap < mb_info->mmap_addr + mb_info->mmap_length)
{
if (mmap->type == 1) /* Usable */
{
uint64_t base = mmap->addr;
uint64_t len = mmap->len;
for (uint64_t addr = base; addr < base + len; addr += PAGE_SIZE)
{
if (addr >= 0x210000) /* Skip first 2.1MB, or ≈ 2.06MiB */
{
uint32_t idx = addr / PAGE_SIZE;
if (idx >= total_pages)
{
continue; /* skip entries above 4GB */
}
clear_bit(idx);
used_pages--;
}
}
}
mmap = (multiboot_memory_map_t*)((uint32_t)mmap + mmap->size + sizeof(mmap->size));
}
}
void* alloc_page(void) {
for (uint32_t i = 0; i < total_pages; i++)
{
if (!test_bit(i))
{
set_bit(i);
used_pages++;
return (void*)(i * PAGE_SIZE);
}
}
return NULL; /* Out of memory */
}
void free_page(void* ptr)
{
uint32_t idx = (uint32_t)ptr / PAGE_SIZE;
clear_bit(idx);
}

183
lib/printf.c Normal file
View File

@ -0,0 +1,183 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <printf.h>
void printwc(const char* str, uint8_t color)
{
uint8_t c = terminal_getcolor();
terminal_setcolor(color);
printf(str);
terminal_setcolor(c);
}
void printd(const char* str)
{
terminal_debug_writestring(str);
}
void printdc(const char* str, uint8_t color)
{
uint8_t c = terminal_getcolor();
terminal_setcolor(color);
printd(str);
terminal_setcolor(c);
}
void printf(const char* format, ...) {
va_list args;
va_start(args, format);
for (size_t i = 0; format[i] != '\0'; ++i) {
if (format[i] == '%' && format[i + 1] != '\0') {
++i;
// Parse width (only supports zero-padding like %04x)
int width = 0;
if (format[i] == '0') {
++i;
while (format[i] >= '0' && format[i] <= '9') {
width = width * 10 + (format[i] - '0');
++i;
}
}
switch (format[i]) {
case 's': {
const char* str = va_arg(args, const char*);
terminal_writestring(str ? str : "(null)");
break;
}
case 'c': {
char c = (char) va_arg(args, int);
terminal_putchar(c);
break;
}
case 'd':
case 'i': {
int32_t val = va_arg(args, int32_t);
print_int(val);
break;
}
case 'u': {
uint32_t val = va_arg(args, uint32_t);
print_uint(val);
break;
}
case 'x': {
uint32_t val = va_arg(args, uint32_t);
print_hex(val, width, false);
break;
}
case 'X': {
uint32_t val = va_arg(args, uint32_t);
print_hex(val, width, true);
break;
}
case 'p': {
void* ptr = va_arg(args, void*);
terminal_writestring("0x");
print_hex((uint32_t)(uintptr_t)ptr, 8, true); // assumes 32-bit pointer
break;
}
case 'f':
case 'F': {
double val = va_arg(args, double); // double is promoted from float
print_double(val, 2); // 2 decimal places
break;
}
case '%': {
terminal_putchar('%');
break;
}
default: {
terminal_putchar('%');
terminal_putchar(format[i]);
break;
}
}
} else {
terminal_putchar(format[i]);
}
}
va_end(args);
}
void print_double(double value, int precision)
{
// Handle the integer part
int32_t integer_part = (int32_t)value;
double fractional_part = value - integer_part;
// Print the integer part
print_int(integer_part);
// Print the decimal point
terminal_putchar('.');
// Print the fractional part (scaled up)
fractional_part *= 1;
for (int i = 0; i < precision; i++) {
fractional_part *= 10;
}
int32_t frac_int = (int32_t)fractional_part;
print_int(frac_int);
}
void print_uint(uint32_t value) {
char buffer[11]; // Enough for 32-bit unsigned int
int i = 0;
do {
buffer[i++] = '0' + (value % 10);
value /= 10;
} while (value > 0);
while (i--) {
terminal_putchar(buffer[i]);
}
}
void print_int(int32_t value) {
char buffer[12]; // Enough for 32-bit signed int (-2147483648)
int i = 0;
uint32_t u;
if (value < 0) {
terminal_putchar('-');
u = (uint32_t)(-value);
} else {
u = (uint32_t)value;
}
// Convert to string in reverse
do {
buffer[i++] = '0' + (u % 10);
u /= 10;
} while (u > 0);
// Print in correct order
while (i--) {
terminal_putchar(buffer[i]);
}
}
void print_hex(uint32_t value, int width, bool uppercase)
{
const char* hex_chars = uppercase ? "0123456789ABCDEF" : "0123456789abcdef";
char buffer[9]; // 8 hex digits max for 32-bit
int i = 0;
do {
buffer[i++] = hex_chars[value & 0xF];
value >>= 4;
} while (value || i < width); // ensure at least 'width' digits
while (i--) {
terminal_putchar(buffer[i]);
}
}

5
lib/processes.c Normal file
View File

@ -0,0 +1,5 @@
#include <stdint.h>
#include <processes.h>

160
lib/string.c Normal file
View File

@ -0,0 +1,160 @@
#include <string.h>
#include <vector_extentions/sse.h>
extern int16_t sse_initialized;
size_t strlen(const char* str)
{
size_t len = 0;
while (str[len]) len++;
return len;
}
size_t strcmp(const char *s1, const char *s2)
{
int32_t i = 0;
while ((s1[i] == s2[i]))
{
if (s2[i++] == 0)
{
return 0;
}
}
return 1;
}
int32_t strncmp(const char *s1, const char *s2, size_t n)
{
while (n--)
{
unsigned char c1 = (unsigned char)*s1++;
unsigned char c2 = (unsigned char)*s2++;
if (c1 != c2)
{
return c1 - c2;
}
if (c1 == '\0')
{
break;
}
}
return 0;
}
size_t strcpy(char *dst, const char *src)
{
int32_t i = 0;
while ((*dst++ = *src++) != 0)
i++;
return i;
}
char *strncpy(char *dest, const char *src, uint32_t n)
{
if (sse_initialized > 0)
{
return sse2_strncpy(dest, src, n);
}
uint32_t i = 0;
for (; i < n && src[i]; ++i)
dest[i] = src[i];
for (; i < n; ++i)
dest[i] = '\0';
return dest;
}
void strcat(char *dest, const char *src)
{
char *end = (char *)dest + strlen(dest);
memcpy((void *)end, (void *)src, strlen(src));
end = end + strlen(src);
*end = '\0';
}
void *memset(void *dst, char c, uint32_t n)
{
char *temp = dst;
for (; n != 0; n--) *temp++ = c;
return dst;
}
void *memcpy(void *dst, const void *src, uint32_t n)
{
if (sse_initialized > 0)
{
return sse2_memcpy(dst, src, n);
}
char *ret = dst;
char *p = dst;
const char *q = src;
while (n--)
*p++ = *q++;
return ret;
}
int32_t memcmp(uint8_t *s1, uint8_t *s2, uint32_t n)
{
while (n--)
{
if (*s1 != *s2)
return 0;
s1++;
s2++;
}
return 1;
}
void* memclr(const void * const m_start, const size_t m_count)
{
if (sse_initialized > 0)
{
return memclr_sse2(m_start, m_count);
}
return memset(m_start, '\0', (uint32_t)m_count);
}
int32_t isspace(char c)
{
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
}
int32_t isalpha(char c)
{
return (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')));
}
int32_t ischar(int32_t c)
{
return ((c >= 32) && (c <= 126));
}
char upper(char c)
{
if ((c >= 'a') && (c <= 'z'))
return (c - 32);
return c;
}
char toupper(char c)
{
if ((c >= 'a') && (c <= 'z'))
{
return (c - 32);
}
return c;
}
char lower(char c)
{
if ((c >= 'A') && (c <= 'Z'))
return (c + 32);
return c;
}

4
lib/syscall.c Normal file
View File

@ -0,0 +1,4 @@
#include <stdint.h>
#include <syscall.h>

263
lib/vector_extentions/sse.c Normal file
View File

@ -0,0 +1,263 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <vector_extentions/sse.h>
void enable_sse(void) {
uint32_t cr0, cr4;
__asm__ volatile ("mov %%cr0, %0" : "=r"(cr0));
cr0 &= ~(1 << 2); // EM = 0
cr0 |= (1 << 1); // MP = 1
__asm__ volatile ("mov %0, %%cr0" :: "r"(cr0));
__asm__ volatile ("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1 << 9); // OSFXSR = 1
cr4 |= (1 << 10); // OSXMMEXCPT = 1
__asm__ volatile ("mov %0, %%cr4" :: "r"(cr4));
}
// Basic SSE test: add two arrays of 4 floats using xmm registers
__attribute__((force_align_arg_pointer))
int32_t test_sse(void) {
float a[4] __attribute__((aligned(16))) = {1.0f, 2.0f, 3.0f, 4.0f};
float b[4] __attribute__((aligned(16))) = {5.0f, 6.0f, 7.0f, 8.0f};
float result[4] __attribute__((aligned(16)));
asm volatile (
"movaps %1, %%xmm0\n\t"
"movaps %2, %%xmm2\n\t"
"addps %%xmm2, %%xmm0\n\t"
"movaps %%xmm0, %0\n\t"
: "=m" (result)
: "m" (a), "m" (b)
: "xmm0", "xmm2"
);
if (result[0] != 6.0 || result[1] != 8.0 || result[2] != 10.0 || result[3] != 12.0)
{
return -1;
}
return 0;
}
__attribute__((force_align_arg_pointer))
void sse2_add_double_arrays(double *dst, const double *a, const double *b, size_t count)
{
for (size_t i = 0; i < count; i += 2)
{
asm volatile (
"movapd (%1), %%xmm0\n\t"
"movapd (%2), %%xmm1\n\t"
"addpd %%xmm1, %%xmm0\n\t"
"movapd %%xmm0, (%0)\n\t"
:
: "r"(dst + i), "r"(a + i), "r"(b + i)
: "xmm0", "xmm1", "memory"
);
}
}
__attribute__((force_align_arg_pointer))
void sse2_add_int64_arrays(int64_t *dst, const int64_t *a, const int64_t *b, size_t count)
{
for (size_t i = 0; i < count; i += 2)
{
asm volatile (
"movdqa (%1), %%xmm0\n\t"
"movdqa (%2), %%xmm1\n\t"
"paddq %%xmm1, %%xmm0\n\t"
"movdqa %%xmm0, (%0)\n\t"
:
: "r"(dst + i), "r"(a + i), "r"(b + i)
: "xmm0", "xmm1", "memory"
);
}
}
__attribute__((force_align_arg_pointer))
void sse2_add_int32_arrays(int32_t *dst, const int32_t *a, const int32_t *b, size_t count)
{
for (size_t i = 0; i < count; i += 4)
{
asm volatile (
"movdqa (%1), %%xmm0\n\t"
"movdqa (%2), %%xmm1\n\t"
"paddd %%xmm1, %%xmm0\n\t"
"movdqa %%xmm0, (%0)\n\t"
:
: "r"(dst + i), "r"(a + i), "r"(b + i)
: "xmm0", "xmm1", "memory"
);
}
}
__attribute__((force_align_arg_pointer))
void *sse2_memcpy(void *dst, const void *src, uint32_t n)
{
uintptr_t i;
/* Align to 16 bytes if necessary */
while (((uintptr_t)dst & 15) && n > 0)
{
*((uint8_t*)dst) = *((uint8_t*)src);
dst = (uint8_t*)dst + 1;
src = (uint8_t*)src + 1;
n--;
}
/* Copy 16-byte chunks with SSE2 */
for (i = 0; i + 15 < n; i += 16)
{
asm volatile (
"movdqa (%1), %%xmm0\n\t" /* Load 16 bytes from source into xmm0 */
"movdqa %%xmm0, (%0)\n\t" /* Store 16 bytes to destination */
:
: "r"(dst + i), "r"(src + i)
: "xmm0", "memory"
);
}
/* Handle remaining bytes (less than 16) */
while (n > 0)
{
*((uint8_t*)dst) = *((uint8_t*)src);
dst = (uint8_t*)dst + 1;
src = (uint8_t*)src + 1;
n--;
}
return dst; /* Return pointer to destination */
}
__attribute__((force_align_arg_pointer))
char *sse2_strncpy(char *dest, const char *src, uint32_t n)
{
uint32_t i = 0;
/* Align initial copy */
while (((uintptr_t)(dest + i) & 15) && i < n && src[i]) {
dest[i] = src[i];
i++;
}
/* Bulk copy in 16-byte blocks */
for (; i + 15 < n; i += 16)
{
asm volatile (
"movdqu (%1), %%xmm0\n\t"
"movdqu %%xmm0, (%0)\n\t"
:
: "r"(dest + i), "r"(src + i)
: "xmm0", "memory"
);
/* Manually check if any nulls in the just-copied block */
for (int j = 0; j < 16; ++j)
{
if (src[i + j] == '\0')
{
/* Null found: pad the rest with zeros */
for (int k = j + 1; k < 16 && i + k < n; ++k)
{
dest[i + k] = '\0';
}
i += 16;
goto tail;
}
}
}
tail:
/* Final bytes */
for (; i < n && src[i]; ++i)
{
dest[i] = src[i];
}
for (; i < n; ++i)
{
dest[i] = '\0';
}
return dest;
}
__attribute__((force_align_arg_pointer))
void double_vector_to_int_vector(const double *src, int32_t *dst)
{
asm volatile (
"pxor %%xmm0, %%xmm0\n\t" /* zero xmm0 */
"cvttpd2dq (%1), %%xmm1\n\t" /* convert src to int32s */
"movq %%xmm1, %%xmm0\n\t" /* move low 64 bits (2 ints) to xmm0 */
"movdqa %%xmm0, (%0)\n\t" /* store result */
:
: "r"(dst), "r"(src)
: "xmm0", "xmm1", "memory"
);
}
__attribute__((force_align_arg_pointer))
void int_vector_to_double_vector(const int32_t *src, double *dst)
{
asm volatile (
"movq (%1), %%xmm0\n\t" /* Load 2 int32s (64 bits) into xmm0 */
"cvtdq2pd %%xmm0, %%xmm1\n\t" /* Convert to 2 doubles */
"movapd %%xmm1, (%0)\n\t" /* Store to destination */
:
: "r"(dst), "r"(src)
: "xmm0", "xmm1", "memory"
);
}
void * memclr_sse2(const void * const m_start, const size_t m_count)
{
/* "i" is our counter of how many bytes we've cleared */
size_t i;
/* find out if "m_start" is aligned on a SSE_XMM_SIZE boundary */
if ((size_t)m_start & (SSE_XMM_SIZE - 1))
{
i = 0;
/* we need to clear byte-by-byte until "m_start" is aligned on an SSE_XMM_SIZE boundary */
/* ... and lets make sure we don't copy 'too' many bytes (i < m_count) */
while (((size_t)m_start + i) & (SSE_XMM_SIZE - 1) && i < m_count)
{
asm volatile ("stosb;" :: "D"((size_t)m_start + i), "a"(0));
i++;
}
}
else
{
/* if "m_start" was aligned, set our count to 0 */
i = 0;
}
asm volatile ("pxor %%xmm0,%%xmm0"::); /* zero out XMM0 */
/* clear 64-byte chunks of memory (4 16-byte operations) */
for(; i + 64 <= m_count; i += 64)
{
asm volatile (" movdqa %%xmm0, 0(%0); " /* move 16 bytes from XMM0 to %0 + 0 */
" movdqa %%xmm0, 16(%0); "
" movdqa %%xmm0, 32(%0); "
" movdqa %%xmm0, 48(%0); "
:: "r"((size_t)m_start + i));
}
/* copy the remaining bytes (if any) */
asm volatile (" rep stosb; " :: "a"((size_t)(0)), "D"(((size_t)m_start) + i), "c"(m_count - i));
/* "i" will contain the total amount of bytes that were actually transfered */
i += m_count - i;
/* we return "m_start" + the amount of bytes that were transfered */
return (void *)(((size_t)m_start) + i);
}

71
linker.ld Normal file
View File

@ -0,0 +1,71 @@
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
ENTRY(_start)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* It used to be universally recommended to use 1M as a start offset,
as it was effectively guaranteed to be available under BIOS systems.
However, UEFI has made things more complicated, and experimental data
strongly suggests that 2M is a safer place to load. In 2016, a new
feature was introduced to the multiboot2 spec to inform bootloaders
that a kernel can be loaded anywhere within a range of addresses and
will be able to relocate itself to run from such a loader-selected
address, in order to give the loader freedom in selecting a span of
memory which is verified to be available by the firmware, in order to
work around this issue. This does not use that feature, so 2M was
chosen as a safer option than the traditional 1M. */
. = 2M;
/* First put the multiboot header, as it is required to be put very early
in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */
.text BLOCK(4K) : ALIGN(4K)
{
KEEP(*(.multiboot))
*(.text)
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
/* Read-write data (uninitialized) and stack */
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
/* Include the list of initialization functions sorted. */
.init_array :
{
crti.o(.init_array)
KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .init_array.*)))
KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .init_array))
crtn.o(.init_array)
}
/* Include the list of termination functions sorted. */
.fini_array :
{
crti.o(.fini_array)
KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .fini_array.*)))
KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .fini_array))
crtn.o(.fini_array)
}
/* The compiler may produce other sections, by default it will put them in
a segment with the same name. Simply add stuff here as needed. */
}

62
misc_asm.s Normal file
View File

@ -0,0 +1,62 @@
/*
A bunch of functions I could have written in C (or not), but decided not to,
Mostly so I could get more experience in assembler.
*/
/*
DO NOT USE "pusha" AND/OR "popa"!!!
THEY ARE DEPRECATED (or something like that)
AND SHOULD NOT BE USED!!!
INSTEAD PUSH/POP REGISTERS ON/OFF THE STACK INDIVIDUALLY!!!
*/
.section .text
.global _enable_paging_asm
.global _push_regs
.global _pop_regs
.global _hang_asm
_enable_paging_asm:
push %eax
mov %cr0, %eax
or $0x80000000, %eax
mov %eax, %cr0
pop %eax
ret
_push_regs:
push %eax
push %ebx
push %ecx
push %edx
push %esi
push %edi
push %esp
push %ebp
ret
_pop_regs:
pop %ebp
pop %esp
pop %edi
pop %esi
pop %edx
pop %ecx
pop %ebx
pop %eax
ret
_hang_asm:
hlt
jmp _hang_asm
.section .data

132
pit.asm Normal file
View File

@ -0,0 +1,132 @@
; NOTE: the code in this section is mostly copied from the OSDev wiki. (wiki.osdev.org) {though osdev.org/osdev.com works just fine.}
section .bss
system_timer_fractions: resd 1 ; Fractions of 1 ms since timer initialized
system_timer_ms: resd 1 ; Number of whole ms since timer initialized
IRQ0_fractions: resd 1 ; Fractions of 1 ms between IRQs
IRQ0_ms: resd 1 ; Number of whole ms between IRQs
IRQ0_frequency: resd 1 ; Actual frequency of PIT
PIT_reload_value: resw 1 ; Current PIT reload value
section .text
global IRQ0_handler
global init_PIT
IRQ0_handler:
push eax
push ebx
mov eax, [IRQ0_fractions]
mov ebx, [IRQ0_ms] ; eax.ebx = amount of time between IRQs
add [system_timer_fractions], eax ; Update system timer tick fractions
adc [system_timer_ms], ebx ; Update system timer tick milli-seconds
mov al, 0x20
out 0x20, al ; Send the EOI to the PIC
pop ebx
pop eax
iretd
;Input
; ebx Desired PIT frequency in Hz
init_PIT:
pushad
; Do some checking
mov eax,0x10000 ;eax = reload value for slowest possible frequency (65536)
cmp ebx,18 ;Is the requested frequency too low?
jbe .gotReloadValue ; yes, use slowest possible frequency
mov eax,1 ;ax = reload value for fastest possible frequency (1)
cmp ebx,1193181 ;Is the requested frequency too high?
jae .gotReloadValue ; yes, use fastest possible frequency
; Calculate the reload value
mov eax,3579545
mov edx,0 ;edx:eax = 3579545
div ebx ;eax = 3579545 / frequency, edx = remainder
cmp edx,3579545 / 2 ;Is the remainder more than half?
jb .l1 ; no, round down
inc eax ; yes, round up
.l1:
mov ebx,3
mov edx,0 ;edx:eax = 3579545 * 256 / frequency
div ebx ;eax = (3579545 * 256 / 3 * 256) / frequency
cmp edx,3 / 2 ;Is the remainder more than half?
jb .l2 ; no, round down
inc eax ; yes, round up
.l2:
; Store the reload value and calculate the actual frequency
.gotReloadValue:
push eax ;Store reload_value for later
mov [PIT_reload_value],ax ;Store the reload value for later
mov ebx,eax ;ebx = reload value
mov eax,3579545
mov edx,0 ;edx:eax = 3579545
div ebx ;eax = 3579545 / reload_value, edx = remainder
cmp edx,3579545 / 2 ;Is the remainder more than half?
jb .l3 ; no, round down
inc eax ; yes, round up
.l3:
mov ebx,3
mov edx,0 ;edx:eax = 3579545 / reload_value
div ebx ;eax = (3579545 / 3) / frequency
cmp edx,3 / 2 ;Is the remainder more than half?
jb .l4 ; no, round down
inc eax ; yes, round up
.l4:
mov [IRQ0_frequency],eax ;Store the actual frequency for displaying later
; Calculate the amount of time between IRQs in 32.32 fixed point
;
; Note: The basic formula is:
; time in ms = reload_value / (3579545 / 3) * 1000
; This can be rearranged in the following way:
; time in ms = reload_value * 3000 / 3579545
; time in ms = reload_value * 3000 / 3579545 * (2^42)/(2^42)
; time in ms = reload_value * 3000 * (2^42) / 3579545 / (2^42)
; time in ms * 2^32 = reload_value * 3000 * (2^42) / 3579545 / (2^42) * (2^32)
; time in ms * 2^32 = reload_value * 3000 * (2^42) / 3579545 / (2^10)
pop ebx ;ebx = reload_value
mov eax,0xDBB3A062 ;eax = 3000 * (2^42) / 3579545
mul ebx ;edx:eax = reload_value * 3000 * (2^42) / 3579545
shrd eax,edx,10
shr edx,10 ;edx:eax = reload_value * 3000 * (2^42) / 3579545 / (2^10)
mov [IRQ0_ms],edx ;Set whole ms between IRQs
mov [IRQ0_fractions],eax ;Set fractions of 1 ms between IRQs
; Program the PIT channel
pushfd
cli ;Disabled interrupts (just in case)
mov al,00110100b ;channel 0, lobyte/hibyte, rate generator
out 0x43, al
mov ax,[PIT_reload_value] ;ax = 16 bit reload value
out 0x40,al ;Set low byte of PIT reload value
mov al,ah ;ax = high 8 bits of reload value
out 0x40,al ;Set high byte of PIT reload value
popfd
popad
ret