10 Commits

Author SHA1 Message Date
aafd4efcb2 Update ata.h
Updated header to match ata.c
2026-01-26 17:18:39 -08:00
d6eb5115d5 Update ata.c
Updated ata.c to include fixed BSY status register
2026-01-26 17:16:31 -08:00
09b2b8cd11 Create ata.c 2026-01-18 17:48:39 -08:00
4079d18a45 Create ata.h
Add base ATA PIO mode driver so that filesystems like fat16 fat32 work.
2026-01-18 17:46:26 -08:00
b6c158957e Merge pull request #95 from shoshta73/qol
[misc] add editorconfig, clangd config and clang-format config
2026-01-18 16:46:05 -08:00
Borna Šoštarić
a7b0d1152f add rules for formatting consecutives 2026-01-18 10:54:18 +01:00
Borna Šoštarić
e38f1aa2ee add .clang-format 2026-01-18 10:40:34 +01:00
Borna Šoštarić
78d5e9a7ab added editorconfig file 2026-01-18 10:18:23 +01:00
Borna Šoštarić
daead5ee57 add clangd config file 2026-01-18 10:15:38 +01:00
Borna Šoštarić
4fb81d2e57 add compile_commands.json genereation 2026-01-18 10:10:39 +01:00
12 changed files with 249 additions and 185 deletions

24
.clang-format Normal file
View File

@@ -0,0 +1,24 @@
BasedOnStyle: Google
IndentWidth: 4
TabWidth: 4
UseTab: Never
ColumnLimit: 80
DerivePointerAlignment: false
PointerAlignment: Right
ReferenceAlignment: Right
AlignConsecutiveMacros: Consecutive
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
IncludeBlocks: Regroup
IncludeCategories:
# Std headers
- Regex: '<[[:alnum:]_.]+>'
Priority: 2
# Other headers
- Regex: '.*'
Priority: 1

6
.clangd Normal file
View File

@@ -0,0 +1,6 @@
CompileFlags:
CompilationDatabase: build
Diagnostics:
UnusedIncludes: Strict
MissingIncludes: Strict

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[Makefile]
indent_style = tab
indent_size = 8
tab_width = 8

View File

@@ -18,9 +18,9 @@ KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC))
KLIBC_SRC = $(wildcard klibc/src/*.c)
KLIBC_OBJ = $(patsubst klibc/src/%.c, $(BUILD_DIR)/klibc/%.o, $(KLIBC_SRC))
.PHONY: all stage1 stage2 kernel compile-commands $(BUILD_DIR)/compile_commands.json run gdb clean clean-cross clean-all
all: $(DISK_IMG)
.PHONY: stage1 stage2 kernel run gdb clean
stage1: $(BUILD_DIR)
$(AS) $(ASFLAGS) -o $(BUILD_DIR)/$@.o bootloader/$@.asm
$(LD) -Ttext=0x7c00 -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.o
@@ -57,6 +57,10 @@ $(BUILD_DIR):
mkdir -p $@
mkdir -p $(BUILD_DIR)/klibc
compile-commands: $(BUILD_DIR)/compile_commands.json
$(BUILD_DIR)/compile_commands.json: $(BUILD_DIR)
bear --output $@ -- make -B
run:
qemu-system-i386 -s -S $(DISK_IMG)

119
kernel/ata.c Normal file
View File

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

44
kernel/ata.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef ATA_H
#define ATA_H
#include <stdint.h>
#include <stdbool.h>
/* ATA I/O ports */
#define ATA_PRIMARY_IO 0x1F0
#define ATA_PRIMARY_CTRL 0x3F6
/* ATA registers */
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SECCOUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_HDDEVSEL 0x06
#define ATA_REG_COMMAND 0x07
#define ATA_REG_STATUS 0x07
/* ATA commands */
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_IDENTIFY 0xEC
/* Status flags */
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DRQ 0x08
#define ATA_SR_ERR 0x01
/* Drive select */
#define ATA_MASTER 0xA0
#define ATA_SLAVE 0xB0
/* Public API */
bool ata_init(void);
bool ata_read_sector(uint32_t lba, uint8_t* buffer);
bool ata_write_sector(uint32_t lba, const uint8_t* buffer);
bool ata_wait_ready(void);
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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