diff --git a/Makefile b/Makefile index 302db5f..540ff0c 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,8 @@ OBJ = boot/boot.o \ drivers/vga.o \ drivers/speaker.o \ drivers/timer.o \ - drivers/keyboard.o + drivers/keyboard.o \ + drivers/mouse.o # Дефолтное правило (просто сборка бинарника) all: $(TARGET) diff --git a/README.md b/README.md index 49aec6a..c1204c7 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ - [x] Нормальный драйвер VGA (скролл итд) - [x] Нормальный драйвер клавиатуры (Caps Lock, Shift, Numpad итд) - [x] Драйвер PC Speaker -- [ ] Драйвер мыши +- [x] Драйвер мыши ## 2. Процессы, память, user mode, еще драйвера diff --git a/drivers/mouse.c b/drivers/mouse.c new file mode 100644 index 0000000..bc23f04 --- /dev/null +++ b/drivers/mouse.c @@ -0,0 +1,123 @@ +#include "mouse.h" +#include "../kernel/io.h" +#include "../kernel/string.h" +#include "../drivers/serial.h" // Для kprint_serial + +#define PS2_DATA 0x60 +#define PS2_STATUS 0x64 +#define PS2_COMMAND 0x64 + +static void ps2_wait_write(void) { + while (inb(PS2_STATUS) & 0x02); +} + +static void ps2_wait_read(void) { + while (!(inb(PS2_STATUS) & 0x01)); +} + +static void mouse_write(uint8_t data) { + ps2_wait_write(); + outb(PS2_COMMAND, 0xD4); // Сигнал: шлем команду именно мыши + ps2_wait_write(); + outb(PS2_DATA, data); +} + +static uint8_t mouse_read(void) { + ps2_wait_read(); + return inb(PS2_DATA); +} + +void mouse_init(void) { + uint8_t status; + + kprint_serial("Initializing PS/2 Mouse...\n"); + + // Включаем второй порт PS/2 (мышь) + ps2_wait_write(); + outb(PS2_COMMAND, 0xA8); + io_wait(); + + // Читаем Command Byte, чтобы включить прерывания + ps2_wait_write(); + outb(PS2_COMMAND, 0x20); + ps2_wait_read(); + status = inb(PS2_DATA) | 2; // Бит 1 — прерывания IRQ12 от мыши + status &= ~0x20; // Сбрасываем Бит 5 — активируем Mouse Clock + + // Записываем Command Byte обратно + ps2_wait_write(); + outb(PS2_COMMAND, 0x60); + ps2_wait_write(); + outb(PS2_DATA, status); + + // Настраиваем саму мышь + mouse_write(0xF6); // Загрузить дефолтные параметры + mouse_read(); // Читаем ACK (0xFA) + + mouse_write(0xF4); // Включаем передачу пакетов (Data Reporting) + mouse_read(); // Читаем ACK (0xFA) + + kprint_serial("Mouse initialized successfully.\n"); +} + +static uint8_t mouse_cycle = 0; +static uint8_t mouse_packet[3]; +static int mouse_x = 40; // Стартуем по центру текстового экрана 80x25 +static int mouse_y = 12; + +void mouse_handler(void) { + uint8_t status = inb(PS2_STATUS); + + // Проверяем, что в буфере реально есть данные и они от мыши (бит 5 выставлен) + if (!(status & 0x01) || !(status & 0x20)) { + return; + } + + mouse_packet[mouse_cycle++] = inb(PS2_DATA); + + if (mouse_cycle == 3) { + mouse_cycle = 0; + + // Бит 3 первого байта всегда должен быть равен 1 (проверка синхронизации) + if (!(mouse_packet[0] & 0x08)) { + return; + } + + uint8_t flags = mouse_packet[0]; + int offset_x = (int)mouse_packet[1]; + int offset_y = (int)mouse_packet[2]; + + // Обработка знаков смещения (8 бит -> 32 бита с сохранением знака) + if (flags & 0x10) offset_x |= 0xFFFFFF00; + if (flags & 0x20) offset_y |= 0xFFFFFF00; + + mouse_x += offset_x; + mouse_y -= offset_y; // Инвертируем Y, так как в VGA координатах 0 верх + + // Ограничиваем рамками консоли 80x25 + if (mouse_x < 0) mouse_x = 0; + if (mouse_x >= 80) mouse_x = 79; + if (mouse_y < 0) mouse_y = 0; + if (mouse_y >= 25) mouse_y = 24; + + // Чек кнопок + int left_click = flags & 0x01; + int right_click = flags & 0x02; + + // Выводим инфу в UART через itoa + char buf[16]; + + kprint_serial("Mouse: X="); + itoa(mouse_x, buf, 10); + kprint_serial(buf); + + kprint_serial(" Y="); + itoa(mouse_y, buf, 10); + kprint_serial(buf); + + if (left_click) kprint_serial(" [LEFT]"); + if (right_click) kprint_serial(" [RIGHT]"); + + kprint_serial("\n"); + } +} diff --git a/drivers/mouse.h b/drivers/mouse.h new file mode 100644 index 0000000..f42b004 --- /dev/null +++ b/drivers/mouse.h @@ -0,0 +1,9 @@ +#ifndef MOUSE_H +#define MOUSE_H + +#include + +void mouse_init(void); +void mouse_handler(void); + +#endif diff --git a/kernel/idt.c b/kernel/idt.c index 8e3cc7a..981381e 100644 --- a/kernel/idt.c +++ b/kernel/idt.c @@ -3,6 +3,7 @@ #include "../drivers/serial.h" #include "../drivers/timer.h" #include "../drivers/keyboard.h" +#include "../drivers/mouse.h" // Сам массив IDT на 256 векторов и указатель на него struct idt_entry idt[256]; @@ -17,7 +18,7 @@ extern void isr16(); extern void isr17(); extern void isr18(); extern void isr19 extern void isr20(); extern void isr21(); // Аппаратные прерывания железа (IRQ) -extern void irq0(); extern void irq1(); +extern void irq0(); extern void irq1(); extern void irq12(); // Внешняя функция на ассемблере для выполнения 'lidt' extern void idt_load(); @@ -61,6 +62,7 @@ void idt_init(void) { // Вешаем аппаратные прерывания (после перемапливания PIC они будут тут) idt_set_gate(32, (uint32_t)irq0, 0x08, 0x8E); // Таймер PIT idt_set_gate(33, (uint32_t)irq1, 0x08, 0x8E); // Клавиатура + idt_set_gate(44, (uint32_t)irq12, 0x08, 0x8E); // Мышь PS/2 // Загружаем IDTR в процессор idt_load(); @@ -79,6 +81,8 @@ void irq_handler(struct registers regs) { timer_handler(); } else if (regs.int_no == 33) { keyboard_handler(); // <-- Вызываем обработчик клавиатуры при векторе 33 + } else if (regs.int_no == 44) { + mouse_handler(); // <-- Вызываем обработчик клавиатуры при векторе 33 } else { char irq_str[4]; itoa(regs.int_no, irq_str, 10); diff --git a/kernel/interrupt.asm b/kernel/interrupt.asm index fa24462..247f49e 100644 --- a/kernel/interrupt.asm +++ b/kernel/interrupt.asm @@ -4,7 +4,7 @@ bits 32 global idt_load global isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8, isr9, isr10 global isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19, isr20, isr21 -global irq0, irq1 +global irq0, irq1, irq12 ; Импортируем Си-обработчики extern idtp @@ -66,6 +66,7 @@ ISR_ERRCODE 21 IRQ 32, 0 ; IRQ0 - Таймер IRQ 33, 1 ; IRQ1 - Клавиатура +IRQ 44, 12 ; IRQ12 - Мышь (вектор 44) ; Перенаправляем глобальные имена на сгенерированные макросами точки isr0: jmp idt_stub_isr_0 @@ -93,6 +94,7 @@ isr21: jmp idt_stub_isr_21 irq0: jmp idt_stub_irq_0 irq1: jmp idt_stub_irq_1 +irq12: jmp idt_stub_irq_12 ; <-- Сюда процессор прыгнет при векторе 44 ; Общая точка сборки для Исключений Процессора isr_common_stub: diff --git a/kernel/kmain.c b/kernel/kmain.c index 2aebcfb..7734e30 100644 --- a/kernel/kmain.c +++ b/kernel/kmain.c @@ -1,5 +1,6 @@ #include "../drivers/vga.h" #include "../drivers/serial.h" +#include "../drivers/mouse.h" #include "../drivers/timer.h" #include "../drivers/speaker.h" #include "gdt.h" @@ -24,8 +25,14 @@ void kernel_main(void) { pic_clear_mask(0); log_info(LOG_NORMAL, "Unmasking IRQ 1... (keyboard)"); pic_clear_mask(1); + log_info(LOG_NORMAL, "Unmasking IRQ 2... (slave pic)"); + pic_clear_mask(2); + log_info(LOG_NORMAL, "Unmasking IRQ 12... (mouse)"); + pic_clear_mask(12); log_info(LOG_NORMAL, "Registering timer at 100Hz"); timer_init(100); + log_info(LOG_NORMAL, "Registering mouse"); + mouse_init(); log_info(LOG_NORMAL, "Enabling IRQ..."); __asm__ __volatile__("sti"); vga_set_color(VGA_COLOR_LIGHT_GREEN, VGA_COLOR_BLACK);