diff --git a/includes/interrupt/idt.h b/includes/interrupt/idt.h index c5ec1cd..95909f4 100644 --- a/includes/interrupt/idt.h +++ b/includes/interrupt/idt.h @@ -29,7 +29,6 @@ struct idtr { void idt_set_gate(uint8_t vector, uint32_t handler, uint16_t selector, uint8_t type_attr); void idt_init_descriptor(void); -static void idt_load(void); void idt_init(void); void idt_clear(void); diff --git a/includes/pic.h b/includes/interrupt/pic.h similarity index 100% rename from includes/pic.h rename to includes/interrupt/pic.h diff --git a/includes/interrupt/pit.h b/includes/interrupt/pit.h new file mode 100644 index 0000000..80c244e --- /dev/null +++ b/includes/interrupt/pit.h @@ -0,0 +1,40 @@ +#ifndef PIT_H +#define PIT_H + +#define PIT_CHANNEL0 0x40 +#define PIT_CHANNEL1 0x41 +#define PIT_CHANNEL2 0x42 +#define PIT_COMMAND 0x43 +#define PIT_BASE_FREQ 1193182 + +#include + +/* Channels */ +#define PIT_CHANNEL_0 0 +#define PIT_CHANNEL_1 1 +#define PIT_CHANNEL_2 2 + +/* Access modes */ +#define PIT_ACCESS_LATCH 0x00 +#define PIT_ACCESS_LOBYTE 0x10 +#define PIT_ACCESS_HIBYTE 0x20 +#define PIT_ACCESS_LOHI 0x30 + +/* Operating modes */ +#define PIT_MODE_0 0x00 +#define PIT_MODE_1 0x02 +#define PIT_MODE_2 0x04 +#define PIT_MODE_3 0x06 +#define PIT_MODE_4 0x08 +#define PIT_MODE_5 0x0A + +/* Binary mode */ +#define PIT_BINARY 0x00 + +void pit_init(uint32_t frequency); +void pit_set_frequency(uint32_t frequency); +uint16_t pit_calculate_divisor(uint32_t frequency); +uint16_t get_divisor(); +uint32_t get_frequency(); + +#endif \ No newline at end of file diff --git a/includes/timer.h b/includes/timer.h new file mode 100644 index 0000000..c1979dc --- /dev/null +++ b/includes/timer.h @@ -0,0 +1,20 @@ +#ifndef TIMER_H +#define TIMER_H + +#include + +void timer_init(uint32_t frequency); + +void timer_irq_handler(void); + +uint32_t timer_get_ticks(void); + +uint32_t timer_get_seconds(void); + +uint32_t timer_get_frequency(void); + +void timer_sleep_m(uint32_t milliseconds); +void timer_sleep_s(uint32_t seconds); +void timer_sleep(float seconds); + +#endif \ No newline at end of file diff --git a/src/driver/vga.c b/src/driver/vga.c index d475c34..d489ce7 100644 --- a/src/driver/vga.c +++ b/src/driver/vga.c @@ -35,7 +35,7 @@ void vga_print(const char *str) { return; } - for (uint64_t i = 0; str[i] != '\0'; i++) { + for (uint32_t i = 0; str[i] != '\0'; i++) { if (str[i] == '\n') { uint8_t y = vga_get_cursor_y() + 1; vga_set_cursor(0, y); diff --git a/src/interrupt/irq.c b/src/interrupt/irq.c deleted file mode 100644 index 84a97a4..0000000 --- a/src/interrupt/irq.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "irq.h" -#include "vga.h" -#include "vga_lib.h" - -void irq_handler(uint32_t vector) { - vga_print("IRQ: "); - vga_print_hex(vector); - vga_print("\n"); -} \ No newline at end of file diff --git a/src/interrupt/irq.asm b/src/interrupt/irq/irq.asm similarity index 58% rename from src/interrupt/irq.asm rename to src/interrupt/irq/irq.asm index f34115f..9548965 100644 --- a/src/interrupt/irq.asm +++ b/src/interrupt/irq/irq.asm @@ -1,23 +1,5 @@ -; ============================================================ -; IRQ handlers 0-15 -; -; PIC IRQs: -; IRQ 0 -> IDT vector 32 -; IRQ 1 -> IDT vector 33 -; ... -; IRQ 15 -> IDT vector 47 -; -; The IDT uses vectors 32-47, but we pass IRQ numbers -; 0-15 to the C IRQ handler. -; ============================================================ - [BITS 32] - -; ============================================================ -; Export IRQ handlers and IRQ table -; ============================================================ - global irq0 global irq1 global irq2 @@ -37,31 +19,33 @@ global irq15 global irq_table - extern irq_handler ; ============================================================ ; IRQ macro ; -; IRQs do NOT automatically push an error code. +; Hardware IRQs do NOT automatically push an error code. ; -; We push: +; We push only the IRQ number ourselves. ; -; 1. dummy error code -; 2. IRQ number +; Stack when entering irq_common_stub: ; -; This gives us a predictable stack layout. +; [ESP] = IRQ number +; [ESP + 4] = EIP +; [ESP + 8] = CS +; [ESP + 12] = EFLAGS ; ============================================================ %macro IRQ 1 irq%1: - + ; Interrupt gates already clear IF automatically. + ; This CLI is therefore optional. cli - push dword 0 ; dummy error code - push dword %1 ; IRQ number: 0-15 + ; Push IRQ number + push dword %1 jmp irq_common_stub @@ -69,7 +53,7 @@ irq%1: ; ============================================================ -; IRQ entry points +; IRQ handlers ; ============================================================ IRQ 0 @@ -121,29 +105,44 @@ irq_table: ; ============================================================ -; Common IRQ stub +; Common IRQ handler ; ============================================================ section .text irq_common_stub: - ; Save general-purpose registers + ; Save all general-purpose registers pusha - ; Pass pointer to interrupt frame - push esp + ; After PUSHA: + ; + ; [ESP + 0] = EDI + ; [ESP + 4] = ESI + ; [ESP + 8] = EBP + ; [ESP + 12] = original ESP + ; [ESP + 16] = EBX + ; [ESP + 20] = EDX + ; [ESP + 24] = ECX + ; [ESP + 28] = EAX + ; [ESP + 32] = IRQ number + ; [ESP + 36] = EIP + ; [ESP + 40] = CS + ; [ESP + 44] = EFLAGS + + ; Pass IRQ number to C + push dword [esp + 32] + call irq_handler + + ; Remove argument passed to C add esp, 4 - ; Restore general-purpose registers + ; Restore registers popa - ; Remove: - ; - ; 4 bytes = IRQ number - ; 4 bytes = dummy error code - ; - add esp, 8 + ; Remove IRQ number + add esp, 4 + ; Return from interrupt iretd \ No newline at end of file diff --git a/src/interrupt/irq/irq.c b/src/interrupt/irq/irq.c new file mode 100644 index 0000000..33d5b7a --- /dev/null +++ b/src/interrupt/irq/irq.c @@ -0,0 +1,22 @@ +#include "irq.h" +#include "pic.h" +#include "timer.h" +#include "vga.h" +#include "vga_lib.h" + +void irq_handler(uint32_t vector) { + + switch (vector) { + case 0: + timer_irq_handler(); + pic_send_eoi((uint8_t)vector); + break; + + default: + vga_print("IRQ: "); + vga_print_hex(vector); + vga_print("\n"); + + pic_send_eoi((uint8_t)vector); + } +} \ No newline at end of file diff --git a/src/interrupt/isr.asm b/src/interrupt/isr/isr.asm similarity index 100% rename from src/interrupt/isr.asm rename to src/interrupt/isr/isr.asm diff --git a/src/interrupt/isr.c b/src/interrupt/isr/isr.c similarity index 100% rename from src/interrupt/isr.c rename to src/interrupt/isr/isr.c diff --git a/src/interrupt/pit.c b/src/interrupt/pit.c new file mode 100644 index 0000000..1737898 --- /dev/null +++ b/src/interrupt/pit.c @@ -0,0 +1,39 @@ +#include "pit.h" +#include "utils.h" +#include + +uint16_t div; +uint32_t freq; + +void pit_init(uint32_t frequency) { pit_set_frequency(frequency); } + +void pit_set_frequency(uint32_t frequency) { + uint16_t divisor = pit_calculate_divisor(frequency); + + div = divisor; + freq = frequency; + + outb(PIT_COMMAND, 0x34); // ch 0, mode 2, binary + + outb(PIT_CHANNEL0, (uint8_t)(divisor & 0xFF)); // Low byte + outb(PIT_CHANNEL0, (uint8_t)((divisor >> 8) & 0xFF)); // High byte +} + +uint16_t pit_calculate_divisor(uint32_t frequency) { + if (frequency == 0) + return 0; + + uint32_t divisor = PIT_BASE_FREQ / frequency; + + if (divisor < 1) + divisor = 1; + + if (divisor > 65535) // overflow check + divisor = 65535; + + return (uint16_t)divisor; +} + +uint16_t get_divisor() { return div; } + +uint32_t get_frequency() { return freq; } \ No newline at end of file diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 446dac9..775cd3c 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -1,11 +1,18 @@ #include "kernel.h" + #include "idt.h" -#include "panic.h" + #include "pic.h" -#include "utils.h" +#include "pit.h" +#include "timer.h" + #include "vga.h" #include "vga_lib.h" +#include "panic.h" + +#include "utils.h" + void kernel_main() { vga_init(); @@ -20,12 +27,20 @@ void kernel_main() { idt_init(); vga_print_at_end_c("... Ok\n", GREEN); - asm volatile("int $0x22"); + vga_log_info("[", "init", "] pit and timer"); + pic_clear_mask(0); + timer_init(1000); // 1000 Hz + + sti(); - panic("testing panics"); + for (int i = 0; i < 53; i++) { + vga_print("."); + timer_sleep(0.02); + } + vga_print_at_end_c("... Ok\n", GREEN); // timer for (;;) { - asm volatile("cli"); + asm volatile("hlt"); } return; diff --git a/src/kernel/timer.c b/src/kernel/timer.c new file mode 100644 index 0000000..17d661f --- /dev/null +++ b/src/kernel/timer.c @@ -0,0 +1,31 @@ +#include "timer.h" +#include "pit.h" +#include "vga.h" + +static uint32_t ticks = 0; +static uint32_t timer_frequency = 0; + +void timer_init(uint32_t frequency) { + timer_frequency = frequency; + pit_init(frequency); +} + +void timer_irq_handler(void) { ticks++; } + +uint32_t timer_get_ticks(void) { return ticks; } + +uint32_t timer_get_seconds(void) { return ticks / timer_frequency; } + +uint32_t timer_get_frequency(void) { return timer_frequency; } + +void timer_sleep_m(uint32_t milliseconds) { + uint32_t start = timer_get_ticks(); + uint32_t wait_ticks = ((uint32_t)milliseconds * timer_frequency) / 1000; + + while (timer_get_ticks() - start < wait_ticks) { + } +} + +void timer_sleep_s(uint32_t seconds) { timer_sleep_m(seconds * 1000); } + +void timer_sleep(float seconds) { timer_sleep_m((uint32_t)(seconds * 1000)); } \ No newline at end of file