diff --git a/README.md b/README.md index 366c291..d8bb988 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,8 @@ ZalupaCalc 2 - это инновационная операционная сис - [x] Вывод на экран прямоугольников и прочих фигур - [x] Вывод на экран BMP - [x] Вывод на экран текста -- [ ] Двойная буферизация -- [ ] Вывод курсора мыши на экран, движение -- [ ] Кнопки (touch zones, массив в памяти где будут координаты областей и действия при нажатии) +- [x] Двойная буферизация +- [x] Вывод курсора мыши на экран, движение ### 3. Математика diff --git a/src/drawing.c b/src/drawing.c index 1d5d215..2625858 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -1,43 +1,44 @@ #include "drawing.h" #include "multiboot.h" -#include "serial.h" -#include "vmm.h" // Для маппинга +#include "string.h" +#include "heap.h" +#include "vmm.h" #include "font.h" #define TRANSPARENT_COLOR 0xFF00FF uint32_t* fb_ptr = 0; +uint32_t* backbuffer = 0; uint32_t screen_width = 0; uint32_t screen_height = 0; void draw_init(struct multiboot_info* mbi) { if (!(mbi->flags & (1 << 12))) { - kprint_serial("DRAWING: NO FRAMEBUFFER!"); return; } uint32_t fb_phys = (uint32_t)mbi->framebuffer_addr; uint32_t width = mbi->framebuffer_width; uint32_t height = mbi->framebuffer_height; - - // Считаем размер и мапим сразу внутри + uint32_t size = width * height * 4; uint32_t num_pages = (size + 0xFFF) / 0x1000; for (uint32_t i = 0; i < num_pages; i++) { - vmm_map_page(fb_phys + (i * 0x1000), - fb_phys + (i * 0x1000), - 0x3 | 0x10); // Present + RW + PCD + vmm_map_page(fb_phys + (i * 0x1000), + fb_phys + (i * 0x1000), + 0x3 | 0x10); } - // Сохраняем в глобальные переменные модуля fb_ptr = (uint32_t*)fb_phys; screen_width = width; screen_height = height; + + backbuffer = (uint32_t*)kmalloc(1024 * 768 * sizeof(uint32_t)); } void put_pixel(int x, int y, uint32_t color) { if (x >= 0 && x < (int)screen_width && y >= 0 && y < (int)screen_height) { - fb_ptr[y * screen_width + x] = color; + backbuffer[y * screen_width + x] = color; } } @@ -85,27 +86,21 @@ void draw_bmp(uint8_t* data, int x, int y) { int w = dh->width; int h = dh->height; - - // 24-битный BMP: каждый ряд выровнен по 4 байтам - // Считаем размер ряда в байтах + int row_size = (w * 3 + 3) & ~3; uint8_t* pixels = data + bh->offset; for (int i = 0; i < h; i++) { - // BMP хранятся снизу вверх, поэтому i идет от 0 до h, - // а рисуем мы начиная с y + h - 1 int draw_y = y + (h - 1 - i); - + for (int j = 0; j < w; j++) { - // Берем 3 байта BGR uint8_t* pixel_ptr = pixels + (i * row_size) + (j * 3); uint8_t b = pixel_ptr[0]; uint8_t g = pixel_ptr[1]; uint8_t r = pixel_ptr[2]; - - // Собираем в 0x00RRGGBB + uint32_t color = (r << 16) | (g << 8) | b; - + put_pixel(x + j, draw_y, color); } } @@ -114,7 +109,7 @@ void draw_bmp(uint8_t* data, int x, int y) { void draw_cursor_sprite(uint8_t* bmp_data, int x, int y) { struct bmp_header* bh = (struct bmp_header*)bmp_data; struct dib_header* dh = (struct dib_header*)(bmp_data + sizeof(struct bmp_header)); - + uint8_t* pixels = bmp_data + bh->offset; int w = dh->width; int h = dh->height; @@ -125,8 +120,7 @@ void draw_cursor_sprite(uint8_t* bmp_data, int x, int y) { for (int j = 0; j < w; j++) { uint8_t* p = pixels + (i * row_size) + (j * 3); uint32_t color = (p[2] << 16) | (p[1] << 8) | p[0]; - - // Если цвет не прозрачный — рисуем + if (color != TRANSPARENT_COLOR) { put_pixel(x + j, draw_y, color); } @@ -135,13 +129,12 @@ void draw_cursor_sprite(uint8_t* bmp_data, int x, int y) { } void put_char(char c, int x, int y, uint32_t color) { - if (c < 32 || c > 126) c = 32; // Если символ вне диапазона — рисуем пробел - + if (c < 32 || c > 126) c = 32; + const uint8_t* glyph = font8x8[c - 32]; - + for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { - // Если бит в строке установлен — рисуем пиксель if (glyph[i] & (1 << (7 - j))) { put_pixel(x + j, y + i, color); } @@ -154,14 +147,17 @@ void draw_string(const char* str, int x, int y, uint32_t color) { int cursor_y = y; while (*str != '\0') { - // Если встретили символ переноса строки if (*str == '\n') { cursor_x = x; - cursor_y += 10; // 8 пикселей высота буквы + 2 пикселя отступ + cursor_y += 10; } else { put_char(*str, cursor_x, cursor_y, color); - cursor_x += 9; // 8 пикселей ширина буквы + 1 пиксель пробел между ними + cursor_x += 9; } str++; } } + +void flip_buffer() { + memcpy((void*)fb_ptr, (void*)backbuffer, 1024 * 768 * 4); +} diff --git a/src/drawing.h b/src/drawing.h index b0249fd..f622f86 100644 --- a/src/drawing.h +++ b/src/drawing.h @@ -17,5 +17,6 @@ void draw_bmp(uint8_t* data, int x, int y); void draw_cursor_sprite(uint8_t* bmp_data, int x, int y); void put_char(char c, int x, int y, uint32_t color); void draw_string(const char* str, int x, int y, uint32_t color); +void flip_buffer(void); #endif diff --git a/src/heap.c b/src/heap.c index 083f893..33f779d 100644 --- a/src/heap.c +++ b/src/heap.c @@ -1,82 +1,90 @@ #include "heap.h" #include "vmm.h" #include "pmm.h" -#include "serial.h" static heap_chunk_t* heap_start_chunk = NULL; -void kheap_init(void) { - kprint_serial("HEAP: Initializing Kernel Heap...\n"); +static uint32_t current_heap_top = HEAP_START + PAGE_SIZE; - // 1. Выделяем одну физ. страницу под старт кучи +int heap_expand(size_t size) { + size_t total_needed = size + sizeof(heap_chunk_t); + size_t pages_needed = (total_needed + PAGE_SIZE - 1) / PAGE_SIZE; + + heap_chunk_t* new_chunk = (heap_chunk_t*)current_heap_top; + + for (size_t i = 0; i < pages_needed; i++) { + void* phys = pmm_alloc_page(); + if (!phys) { + return 0; + } + + vmm_map_page((uint32_t)phys, current_heap_top, 3); + + if (i < pages_needed - 1) { + current_heap_top += PAGE_SIZE; + } + } + + new_chunk->size = (pages_needed * PAGE_SIZE) - sizeof(heap_chunk_t); + new_chunk->is_free = 1; + new_chunk->next = NULL; + + heap_chunk_t* curr = heap_start_chunk; + while (curr->next != NULL) { + curr = curr->next; + } + curr->next = new_chunk; + + current_heap_top += PAGE_SIZE; + return 1; +} + +void kheap_init(void) { void* phys_page = pmm_alloc_page(); - - // 2. Мапим её на виртуальный адрес HEAP_START - // Нам нужны флаги Present (1) + Write (2) = 3 + if (phys_page == NULL) { + while(1); + } + vmm_map_page((uint32_t)phys_page, HEAP_START, 3); - // 3. Создаем базовый блок на всю эту страницу heap_start_chunk = (heap_chunk_t*)HEAP_START; heap_start_chunk->size = PAGE_SIZE - sizeof(heap_chunk_t); heap_start_chunk->is_free = 1; heap_start_chunk->next = NULL; - - kprint_serial("HEAP: Kernel Heap initialized successfully at 0x00400000!\n"); } void* kmalloc(size_t size) { if (size == 0) return NULL; - - // Выравниваем размер по 4 байта для стабильности на x86 size = (size + 3) & ~3; heap_chunk_t* current = heap_start_chunk; - // Ищем подходящий свободный блок (First Fit) while (current != NULL) { if (current->is_free && current->size >= size) { - - // Если блок сильно больше, чем надо, дробим его - // sizeof(heap_chunk_t) + 4 байта минимально на остаток - if (current->size >= size + sizeof(heap_chunk_t) + 4) { - heap_chunk_t* new_chunk = (heap_chunk_t*)((uint32_t)current + sizeof(heap_chunk_t) + size); - new_chunk->size = current->size - size - sizeof(heap_chunk_t); - new_chunk->is_free = 1; - new_chunk->next = current->next; - - current->size = size; - current->next = new_chunk; - } - current->is_free = 0; - - // Возвращаем указатель на память СРАЗУ ЗА заголовком return (void*)((uint32_t)current + sizeof(heap_chunk_t)); } current = current->next; } - // По-хорошему, если памяти не хватило, нужно выделить еще страницу из PMM - // и расширить кучу. Но для первого теста выкинем панику. - kprint_serial("HEAP ERROR: OUT OF KERNEL HEAP MEMORY!\n"); - return NULL; + if (!heap_expand(size)) { + return NULL; + } + return kmalloc(size); } void kfree(void* ptr) { if (ptr == NULL) return; - // Смещаемся назад, чтобы получить доступ к заголовку блока heap_chunk_t* chunk = (heap_chunk_t*)((uint32_t)ptr - sizeof(heap_chunk_t)); chunk->is_free = 1; - // Склеиваем последовательные свободные блоки (Coalescing), чтобы не было фрагментации heap_chunk_t* current = heap_start_chunk; while (current != NULL) { if (current->is_free && current->next && current->next->is_free) { current->size += sizeof(heap_chunk_t) + current->next->size; current->next = current->next->next; - // Не шагаем вперед, проверяем текущий блок еще раз с новым next - continue; + continue; } current = current->next; } diff --git a/src/heap.h b/src/heap.h index 2c7ab77..20323aa 100644 --- a/src/heap.h +++ b/src/heap.h @@ -5,7 +5,7 @@ #include // Начальный виртуальный адрес кучи ядра (3 МБ) -#define HEAP_START 0x00300000 +#define HEAP_START 0x1400000 // Структура заголовка блока памяти typedef struct heap_chunk { diff --git a/src/idt.c b/src/idt.c index 0bac584..97df52e 100644 --- a/src/idt.c +++ b/src/idt.c @@ -50,8 +50,37 @@ void idt_init(void) { } void isr_handler(struct registers *regs) { - kprint_serial("Exception: "); + kprint_serial("\n!!! EXCEPTION !!!\n"); + kprint_serial(" int_no="); kprint_serial_dec(regs->int_no); + kprint_serial(" err_code="); + kprint_serial_dec(regs->err_code); + kprint_serial("\n EIP=0x"); + kprint_serial_hex(regs->eip); + kprint_serial(" CS=0x"); + kprint_serial_hex(regs->cs); + kprint_serial(" EFLAGS=0x"); + kprint_serial_hex(regs->eflags); + kprint_serial("\n EAX=0x"); + kprint_serial_hex(regs->eax); + kprint_serial(" EBX=0x"); + kprint_serial_hex(regs->ebx); + kprint_serial(" ECX=0x"); + kprint_serial_hex(regs->ecx); + kprint_serial(" EDX=0x"); + kprint_serial_hex(regs->edx); + kprint_serial("\n ESP=0x"); + kprint_serial_hex(regs->esp); + kprint_serial(" EBP=0x"); + kprint_serial_hex(regs->ebp); + kprint_serial(" ESI=0x"); + kprint_serial_hex(regs->esi); + kprint_serial(" EDI=0x"); + kprint_serial_hex(regs->edi); + kprint_serial("\n CR2=0x"); + uint32_t cr2; + __asm__ __volatile__("mov %%cr2, %0" : "=r"(cr2)); + kprint_serial_hex(cr2); kprint_serial("\nSystem Halted.\n"); while(1) asm("hlt"); } @@ -64,9 +93,6 @@ void irq_handler(struct registers *regs) { } else if (regs->int_no == 44) { mouse_handler(); } else { - kprint_serial("Unknown IRQ: "); - kprint_serial_dec(regs->int_no); - kprint_serial("\n"); } if (regs->int_no >= 40) outb(0xA0, 0x20); // Slave diff --git a/src/keyboard.c b/src/keyboard.c index 69861eb..abf2d4b 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1,24 +1,19 @@ #include "keyboard.h" -#include "serial.h" #include "io.h" -// Состояние модификаторов клавиатуры static int is_e0 = 0; static int shift_pressed = 0; static int caps_lock_active = 0; -// Скан-коды управления (Set 1) #define SCAN_LSHIFT 0x2A #define SCAN_RSHIFT 0x36 #define SCAN_CAPS_LOCK 0x3A -// Скан-коды стрелок (после 0xE0) #define SCAN_UP 0x48 #define SCAN_DOWN 0x50 #define SCAN_LEFT 0x4B #define SCAN_RIGHT 0x4D -// Таблица 1: Обычные символы (без Shift) static const char kbdus_lower[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', @@ -29,7 +24,6 @@ static const char kbdus_lower[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -// Таблица 2: Символы при зажатом Shift static const char kbdus_upper[128] = { 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', @@ -44,54 +38,45 @@ void keyboard_init(void) { shift_pressed = 0; caps_lock_active = 0; is_e0 = 0; - kprint_serial("[INFO] Full keyboard driver with Modifiers initialized.\n"); } void keyboard_handler(void) { uint8_t status = inb(0x64); - - // Бит 5 равен 1, если данные пришли от мыши (Auxiliary Device) - // Мышь не должна обрабатываться здесь! + if (status & 0x20) { - // Очистим порт, чтобы не висел, но не обрабатываем как клаву - inb(0x60); + inb(0x60); return; } - // Если мы здесь, значит это точно клавиатура - if (!(status & 0x01)) return; // Нет данных + if (!(status & 0x01)) return; uint8_t scancode = inb(0x60); - // Обработка префикса 0xE0 if (scancode == 0xE0) { is_e0 = 1; return; } - // Обработка Break Code (отпускание) if (scancode & 0x80) { uint8_t released_code = scancode & 0x7F; if (released_code == SCAN_LSHIFT || released_code == SCAN_RSHIFT) { shift_pressed = 0; } - is_e0 = 0; // Сброс префикса + is_e0 = 0; return; } - // Обработка стрелок (расширенные) if (is_e0) { is_e0 = 0; switch (scancode) { - case SCAN_UP: /* ... */ break; - case SCAN_DOWN: /* ... */ break; - case SCAN_LEFT: /* ... */ break; - case SCAN_RIGHT: /* ... */ break; + case SCAN_UP: break; + case SCAN_DOWN: break; + case SCAN_LEFT: break; + case SCAN_RIGHT: break; } return; } - // Обычные модификаторы if (scancode == SCAN_LSHIFT || scancode == SCAN_RSHIFT) { shift_pressed = 1; return; @@ -102,7 +87,6 @@ void keyboard_handler(void) { return; } - // Печать символов if (scancode < 128) { char c = 0; int use_upper = shift_pressed; @@ -113,9 +97,6 @@ void keyboard_handler(void) { c = (use_upper) ? kbdus_upper[scancode] : kbdus_lower[scancode]; - if (c != 0) { - char str[2] = {c, 0}; - kprint_serial(str); - } + (void)c; } } diff --git a/src/kmain.c b/src/kmain.c index acdf467..6d9d682 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -9,215 +9,65 @@ #include "pmm.h" #include "vmm.h" #include "pic.h" -#include "logger.h" #include "string.h" #include "multiboot.h" -uint32_t bg_color = 0x00008080; -uint32_t color_sky_blue = 0x0087CEEB; -uint32_t color_orange = 0x00FFA500; uint32_t color_white = 0x00FFFFFF; -uint32_t color_black = 0x00000000; +extern uint32_t* fb_ptr; +extern uint32_t* backbuffer; +extern uint32_t screen_width; +extern uint32_t screen_height; extern uint8_t _binary_assets_logo_bmp_start[]; extern uint8_t _binary_assets_bg_bmp_start[]; extern uint8_t _binary_assets_cursor_bmp_start[]; -uint32_t frame_count = 0; // Сколько раз отрисовали за секунду -uint32_t last_fps_ticks = 0; // Когда последний раз обновляли FPS -uint32_t current_fps = 0; // Результат (число FPS) +uint32_t frame_count = 0; +uint32_t last_fps_ticks = 0; +uint32_t current_fps = 0; -extern uint32_t _kernel_end; // Символ 'end' прописан в конце любого стандартного linker.ld +extern uint32_t _kernel_end; uint32_t kernel_end = (uint32_t)&_kernel_end; void kernel_main(uint32_t mboot_addr, uint32_t magic) { - log_info(LOG_NORMAL, "Initializing serial..."); serial_init(); if (magic != MULTIBOOT_BOOTLOADER_MAGIC) { - log_error(LOG_NORMAL, "fuck, i got invalid multiboot magic. IS THAT REALLY THE MULTIBOOT YOU TALKED ABOUT?"); while(1); } struct multiboot_info* mbi = (struct multiboot_info*)mboot_addr; if (!(mbi->flags & (1 << 6))) { - log_error(LOG_NORMAL, "fuck, i haven't got a memory map. IS THAT REALLY THE MULTIBOOT YOU TALKED ABOUT?"); while(1); } - log_info(LOG_NORMAL, "Initializing GDT..."); gdt_init(); - log_info(LOG_NORMAL, "Initializing IDT..."); idt_init(); - log_info(LOG_NORMAL, "Remapping PIC..."); pic_remap(32, 40); - log_info(LOG_NORMAL, "Unmasking IRQ 0... (timer)"); 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(); - kprint_serial("--------------------\n"); - log_info(LOG_NORMAL, "Enabling PMM..."); - pmm_init(mbi, (uint32_t)&_kernel_end); - kprint_serial("--- Memory Map From GRUB ---"); - struct multiboot_mmap_entry* mmap = (struct multiboot_mmap_entry*)mbi->mmap_addr; - - while((uint32_t)mmap < mbi->mmap_addr + mbi->mmap_length) { - char addr_buf[32]; - char len_buf[32]; - - itoa((uint32_t)mmap->addr, addr_buf, 16); - itoa((uint32_t)mmap->len, len_buf, 10); - - kprint_serial("Region: Base=0x"); - kprint_serial(addr_buf); - kprint_serial(" Size="); - kprint_serial(len_buf); - - if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) { - kprint_serial(" [FREE RAM]\n"); - } else { - kprint_serial(" [RESERVED]\n"); - } - - // Переходим к следующей записи (учитываем поле size) - mmap = (struct multiboot_mmap_entry*)((uint32_t)mmap + mmap->size + sizeof(mmap->size)); - } - kprint_serial("--- trying to allocate some fucking memory... ---\n"); - kprint_serial("page 1... "); - void* p1 = pmm_alloc_page(); - kprint_serial("page 2..."); - void* p2 = pmm_alloc_page(); - kprint_serial("ok!"); - char buf[32]; - kprint_serial("PMM TEST: Page 1 allocated at 0x"); - itoa((uint32_t)p1, buf, 16); - kprint_serial(buf); - kprint_serial("\n"); - - kprint_serial("PMM TEST: Page 2 allocated at 0x"); - itoa((uint32_t)p2, buf, 16); - kprint_serial(buf); - kprint_serial("\n"); - - kprint_serial("freeing the pages back... page 1... "); - pmm_free_page(p1); - kprint_serial("page 2... "); - pmm_free_page(p2); - kprint_serial("it don't died!\n"); - - kprint_serial("\n--- PMM MEMORY STATS ---\n"); - - pmm_get_stats(); // Просто запускаем сбор, она запишет всё в глобальную переменную - - // Берем указатель на глобальную переменную - pmm_stats_t* my_stats = pmm_get_stats_ptr(); - - kprint_serial("Total Memory: "); - itoa(my_stats->total_memory >> 20, buf, 10); - kprint_serial(buf); - kprint_serial(" MB\n"); - - kprint_serial("Used Memory: "); - itoa(my_stats->used_memory >> 10, buf, 10); - kprint_serial(buf); - kprint_serial(" KB\n"); - - kprint_serial("Free Memory: "); - itoa(my_stats->free_memory >> 20, buf, 10); - kprint_serial(buf); - kprint_serial(" MB\n"); - - kprint_serial("------------------------\n\n"); - log_info(LOG_NORMAL, "Enabling VMM..."); + pmm_init(mbi, (uint32_t)&_kernel_end); vmm_init(); - log_info(LOG_NORMAL, "Enabling KHeap..."); - kheap_init(); - - kprint_serial("--- HEAP TESTING ---\n"); - - // Выделяем три разных куска памяти - int* arr1 = (int*)kmalloc(10 * sizeof(int)); - char* str = (char*)kmalloc(64); - int* arr2 = (int*)kmalloc(5 * sizeof(int)); - char hbuf[32]; - kprint_serial("Allocated arr1 at virtual address: 0x"); - itoa((uint32_t)arr1, hbuf, 16); - kprint_serial(hbuf); - kprint_serial("\n"); - kprint_serial("Allocated str at virtual address: 0x"); - itoa((uint32_t)str, hbuf, 16); - kprint_serial(hbuf); - kprint_serial("\n"); - // Запишем туда данные, чтобы проверить, что маппинг страниц не падает при записи - arr1[0] = 1337; - str[0] = 'H'; - str[1] = 'A'; - str[2] = 'I'; - str[3] = '\0'; - kprint_serial("Data integrity check: str is "); - kprint_serial(str); - kprint_serial("\n"); - // Освобождаем память обратно - kfree(arr1); - kfree(str); - kfree(arr2); - - kprint_serial("Kfree done. It don't died twice!\n"); - if (mbi->flags & (1 << 12)) { - log_info(LOG_NORMAL, "Framebuffer detected!"); - - uint32_t fb_addr = mbi->framebuffer_addr; - uint32_t width = mbi->framebuffer_width; - uint32_t height = mbi->framebuffer_height; - uint32_t bpp = mbi->framebuffer_bpp; - - char buf[16]; - kprint_serial("FB Addr: "); - kprint_serial_hex(fb_addr); - kprint_serial("\nSize: "); - kprint_serial_dec(width); - kprint_serial("x"); - kprint_serial_dec(height); - kprint_serial("\n"); + kheap_init(); + + if (mbi->flags & (1 << 12)) { draw_init(mbi); - } else { - log_error(LOG_NORMAL, "No Framebuffer provided by GRUB!"); } - log_info(LOG_NORMAL, "Enabling IRQ..."); + __asm__ __volatile__("sti"); - speaker_beep(100, 10); - speaker_beep(125, 10); - speaker_beep(150, 10); - speaker_beep(175, 10); - speaker_beep(200, 10); - speaker_beep(225, 10); - speaker_beep(250, 10); - speaker_beep(275, 10); - speaker_beep(300, 10); - speaker_beep(325, 10); - speaker_beep(350, 400); - log_info(LOG_NORMAL, "OK!"); - int old_mouse_x = 0; - int old_mouse_y = 0; + char fpsbuf[4]; while (1) { - // 1. Всегда рисуем фон и элементы draw_bmp(_binary_assets_bg_bmp_start, 0, 0); draw_cursor_sprite(_binary_assets_cursor_bmp_start, mouse_x, mouse_y); - - // 2. Всегда рисуем FPS draw_string("FPS: ", 0, 0, color_white); itoa(current_fps, fpsbuf, 10); draw_string(fpsbuf, 40, 0, color_white); - - // 3. Считаем кадр + + flip_buffer(); + frame_count++; - - // 4. Логика обновления FPS (раз в секунду) + if (system_ticks - last_fps_ticks >= 100) { current_fps = frame_count; frame_count = 0; diff --git a/src/mouse.c b/src/mouse.c index 2b8adb4..346309d 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -1,7 +1,5 @@ #include "mouse.h" #include "io.h" -#include "string.h" -#include "serial.h" // Для kprint_serial #define PS2_DATA 0x60 #define PS2_STATUS 0x64 @@ -17,7 +15,7 @@ static void ps2_wait_read(void) { static void mouse_write(uint8_t data) { ps2_wait_write(); - outb(PS2_COMMAND, 0xD4); // Сигнал: шлем команду именно мыши + outb(PS2_COMMAND, 0xD4); ps2_wait_write(); outb(PS2_DATA, data); } @@ -30,34 +28,26 @@ static uint8_t mouse_read(void) { 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 + status = inb(PS2_DATA) | 2; + status &= ~0x20; - // Записываем 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(0xF6); + mouse_read(); - mouse_write(0xF4); // Включаем передачу пакетов (Data Reporting) - mouse_read(); // Читаем ACK (0xFA) - - kprint_serial("Mouse initialized successfully.\n"); + mouse_write(0xF4); + mouse_read(); } static uint8_t mouse_cycle = 0; @@ -69,61 +59,36 @@ volatile int right_click = 0; void mouse_handler(void) { uint8_t status = inb(PS2_STATUS); - - // Проверяем, что в буфере реально есть данные и они от мыши (бит 5 выставлен) + if (!(status & 0x01) || !(status & 0x20)) { return; } - // ДЕБАГ: выводим каждый байт - kprint_serial("M_BYTE: "); - kprint_serial_hex(status); - kprint_serial("\n"); - mouse_packet[mouse_cycle++] = inb(PS2_DATA); if (mouse_cycle == 3) { mouse_cycle = 0; - // Бит 3 первого байта всегда должен быть равен 1 (проверка синхронизации) if (!(mouse_packet[0] & 0x08)) { - return; + 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 верх + mouse_y -= offset_y; - if (mouse_x < 0) mouse_x = 0; + if (mouse_x < 0) mouse_x = 0; if (mouse_x >= 1024) mouse_x = 1023; if (mouse_y < 0) mouse_y = 0; if (mouse_y >= 768) mouse_y = 767; - // Чек кнопок left_click = flags & 0x01; 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/src/pic.c b/src/pic.c index 6199f21..78cfa2d 100644 --- a/src/pic.c +++ b/src/pic.c @@ -1,55 +1,43 @@ #include "pic.h" #include "io.h" -#include "logger.h" #define PIC1_COMMAND 0x20 #define PIC1_DATA 0x21 #define PIC2_COMMAND 0xA0 #define PIC2_DATA 0xA1 -#define ICW1_INIT 0x10 // Инициализация -#define ICW1_IC4 0x01 // Будет ICW4 -#define ICW4_8086 0x01 // Режим 8086/8186 +#define ICW1_INIT 0x10 +#define ICW1_IC4 0x01 +#define ICW4_8086 0x01 void pic_remap(uint8_t master_offset, uint8_t slave_offset) { - log_info(LOG_DETAIL, "Remapping PIC..."); - - // Сохраняем текущие маски прерываний (какое железо сейчас отключено) uint8_t a1 = inb(PIC1_DATA); uint8_t a2 = inb(PIC2_DATA); - // Свисток к началу инициализации (ICW1) outb(PIC1_COMMAND, ICW1_INIT | ICW1_IC4); io_wait(); outb(PIC2_COMMAND, ICW1_INIT | ICW1_IC4); io_wait(); - // Задаем новые смещения векторов (ICW2) - outb(PIC1_DATA, master_offset); // Master IRQ 0-7 -> вешаем на master_offset (32) + outb(PIC1_DATA, master_offset); io_wait(); - outb(PIC2_DATA, slave_offset); // Slave IRQ 8-15 -> вешаем на slave_offset (40) + outb(PIC2_DATA, slave_offset); io_wait(); - // Связываем Master и Slave между собой (ICW3) - outb(PIC1_DATA, 4); // Сообщаем Master, что Slave сидит на IRQ2 (00000100б = 4) + outb(PIC1_DATA, 4); io_wait(); - outb(PIC2_DATA, 2); // Сообщаем Slave его каскадный ID (2) + outb(PIC2_DATA, 2); io_wait(); - // Включаем режим 8086 (ICW4) outb(PIC1_DATA, ICW4_8086); io_wait(); outb(PIC2_DATA, ICW4_8086); io_wait(); - // Восстанавливаем сохраненные маски outb(PIC1_DATA, a1); outb(PIC2_DATA, a2); - - log_info(LOG_DETAIL, "PIC remapped"); } -// Запретить прерывание от конкретного IRQ void pic_set_mask(uint8_t irq_line) { uint16_t port; if (irq_line < 8) { @@ -62,7 +50,6 @@ void pic_set_mask(uint8_t irq_line) { outb(port, value); } -// Разрешить прерывание от конкретного IRQ void pic_clear_mask(uint8_t irq_line) { uint16_t port; if (irq_line < 8) { diff --git a/src/pmm.c b/src/pmm.c index 62859ec..a0db3cf 100644 --- a/src/pmm.c +++ b/src/pmm.c @@ -1,16 +1,12 @@ #include "pmm.h" #include "string.h" -#include "logger.h" -#include "serial.h" -// Указатель на начало нашей битовой карты в памяти static uint32_t* pmm_bitmap = NULL; static uint32_t pmm_bitmap_size = 0; static uint32_t total_pages = 0; static pmm_stats_t global_stats; -// Вспомогательные инлайны для быстрой работы с битами ядра static inline void bitmap_set(uint32_t page_index) { pmm_bitmap[page_index / 32] |= (1 << (page_index % 32)); } @@ -24,24 +20,19 @@ static inline int bitmap_test(uint32_t page_index) { } void pmm_init(struct multiboot_info* mbi, uint32_t kernel_end) { - // Рассчитываем менеджер на 128 МБ памяти - uint32_t mem_size = 128 * 1024 * 1024; + uint32_t mem_size = 128 * 1024 * 1024; total_pages = mem_size / PAGE_SIZE; - - // Саму карту пихаем прямо за концом ядра (куда указывает линкер) - pmm_bitmap = (uint32_t*)kernel_end; - pmm_bitmap_size = total_pages / 8; // Размер карты в байтах (4096 байт) - + + pmm_bitmap = (uint32_t*)0x1000000; + pmm_bitmap_size = total_pages / 8; + uint32_t bitmap_end = kernel_end + pmm_bitmap_size; - // Шаг 1: Помечаем вообще всё как занятое (1). Безопасность превыше всего. memset(pmm_bitmap, 0xFF, pmm_bitmap_size); - // Шаг 2: Парсим карту памяти от GRUB и открываем доступные регионы struct multiboot_mmap_entry* mmap = (struct multiboot_mmap_entry*)mbi->mmap_addr; - + while((uint32_t)mmap < mbi->mmap_addr + mbi->mmap_length) { - // Если GRUB говорит, что этот кусок RAM свободен для юзания if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) { uint32_t start_addr = (uint32_t)mmap->addr; uint32_t end_addr = start_addr + (uint32_t)mmap->len; @@ -49,46 +40,36 @@ void pmm_init(struct multiboot_info* mbi, uint32_t kernel_end) { for (uint32_t addr = start_addr; addr < end_addr; addr += PAGE_SIZE) { uint32_t page_idx = addr / PAGE_SIZE; if (page_idx < total_pages) { - bitmap_clear(page_idx); // Ставим 0 (страница свободна) + bitmap_clear(page_idx); } } } - // Шагаем к следующей записи в таблице Multiboot mmap = (struct multiboot_mmap_entry*)((uint32_t)mmap + mmap->size + sizeof(mmap->size)); } - // Шаг 3: Жестко защищаем саму ОС! - // Всё, что лежит от физического адреса 0x0 до bitmap_end (код ядра, стек, сама битовая карта), - // мы принудительно забиваем единицами, чтобы аллокатор туда никогда не залез. for (uint32_t addr = 0; addr < bitmap_end; addr += PAGE_SIZE) { bitmap_set(addr / PAGE_SIZE); } - - log_info(LOG_NORMAL, "pmm works."); } void* pmm_alloc_page(void) { - // Тупо линейно ищем первый попавшийся бит, равный 0 for (uint32_t i = 0; i < total_pages; i++) { - if (!bitmap_test(i)) { - bitmap_set(i); // Застолбили страницу (поставили 1) - return (void*)(i * PAGE_SIZE); // Считаем её реальный физический адрес + if (!bitmap_test(i)) { + bitmap_set(i); + return (void*)(i * PAGE_SIZE); } } - - log_error(LOG_NORMAL, "PMM: FUCK, WHERE IS ALL OUR FUCKING MEMORY?!"); - return NULL; + return NULL; } void pmm_free_page(void* addr) { uint32_t page_idx = ((uint32_t)addr) / PAGE_SIZE; if (page_idx < total_pages) { - bitmap_clear(page_idx); // Сбросили в 0, страница снова чиста + bitmap_clear(page_idx); } } void pmm_get_stats(void) { - kprint_serial("Someone wants memory stats! Running the test!\n"); uint32_t used = 0; uint32_t free = 0; @@ -100,20 +81,14 @@ void pmm_get_stats(void) { } } - kprint_serial("Okay, i am collected the stats. "); - - // Пишем в ГЛОБАЛЬНУЮ переменную global_stats.total_pages = total_pages; global_stats.used_pages = used; global_stats.free_pages = free; global_stats.total_memory = total_pages << 12; global_stats.used_memory = used << 12; global_stats.free_memory = free << 12; - - kprint_serial("Goodbye!\n"); } -// Добавим функцию-геттер, которая просто вернет указатель на эту глобальную память pmm_stats_t* pmm_get_stats_ptr(void) { return &global_stats; } diff --git a/src/timer.c b/src/timer.c index 5fb319a..97591e9 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,25 +1,14 @@ #include "timer.h" #include "io.h" -#include "serial.h" -#include "logger.h" -// Глобальный счетчик тиков системы uint32_t system_ticks = 0; void timer_init(uint32_t frequency) { - log_info(LOG_NORMAL, "Initializing PIT Timer..."); - - // Вычисляем делитель частоты uint32_t divisor = 1193182 / frequency; - // Шлем команду: Канал 0, Режим 3 (генератор меандра), доступ по очереди Младший/Старший байт outb(0x43, 0x36); - - // Раскалываем 16-битный делитель на два байта и шлем в порт данных Канала 0 (0x40) outb(0x40, (uint8_t)(divisor & 0xFF)); outb(0x40, (uint8_t)((divisor >> 8) & 0xFF)); - - log_info(LOG_NORMAL, "Timer registered"); } void timer_handler(void) { diff --git a/src/vmm.c b/src/vmm.c index ffd202f..a08d4eb 100644 --- a/src/vmm.c +++ b/src/vmm.c @@ -1,75 +1,77 @@ #include "vmm.h" #include "pmm.h" -#include "serial.h" -// 1. Обязательное выравнивание для CR3 и таблиц страниц uint32_t page_directory[1024] __attribute__((aligned(4096))); uint32_t first_page_table[1024] __attribute__((aligned(4096))); +static uint32_t temp_table_phys = 0; + extern void vmm_enable_paging(uint32_t* page_directory); void vmm_init(void) { - kprint_serial("VMM: Initializing paging...\n"); - - // 2. Очищаем каталог (ставим Not Present) for (int i = 0; i < 1024; i++) { - page_directory[i] = 0x00000002; // Read/Write, Not Present + page_directory[i] = 0x00000002; } - // 3. Identity Map для первых 4 МБ - // i * 4096 дает физический адрес страницы for (uint32_t i = 0; i < 1024; i++) { - first_page_table[i] = (i * 0x1000) | 0x3; // Present + RW + first_page_table[i] = (i * 0x1000) | 0x3; } - // 4. Указываем каталогу на таблицу - // (uint32_t)first_page_table — это уже физический адрес, - // если ядро по 1МБ и мы не используем сложные смещения. page_directory[0] = ((uint32_t)first_page_table) | 0x3; - kprint_serial("DEBUG: page_directory is at: 0x"); - kprint_serial_hex((uint32_t)page_directory); - kprint_serial("\n"); + temp_table_phys = (uint32_t)pmm_alloc_page(); + page_directory[1023] = (temp_table_phys & 0xFFFFF000) | 0x3; - kprint_serial("DEBUG: page_directory[0] contains: 0x"); - kprint_serial_hex(page_directory[0]); - kprint_serial("\n"); + uint32_t* temp_table = (uint32_t*)temp_table_phys; + for (int i = 0; i < 1024; i++) temp_table[i] = 0; - kprint_serial("VMM: Paging configured. Enabling CR0...\n"); + uint32_t bitmap_phys = 0x1000000; + uint32_t bitmap_pd_idx = bitmap_phys >> 22; + uint32_t bitmap_pt_idx = (bitmap_phys >> 12) & 0x3FF; + uint32_t bitmap_pt_phys = (uint32_t)pmm_alloc_page(); + page_directory[bitmap_pd_idx] = (bitmap_pt_phys & 0xFFFFF000) | 0x3; + uint32_t* bitmap_pt = (uint32_t*)bitmap_pt_phys; + for (int i = 0; i < 1024; i++) bitmap_pt[i] = 0; + bitmap_pt[bitmap_pt_idx] = (bitmap_phys & 0xFFFFF000) | 0x3; - // 5. Включаем vmm_enable_paging(page_directory); - - kprint_serial("VMM: FUCK, IT WORKS!\n"); } void vmm_map_page(uint32_t phys, uint32_t virt, uint32_t flags) { - // Выковыриваем индексы из виртуального адреса - uint32_t pd_idx = virt >> 22; // Старшие 10 бит — индекс каталога - uint32_t pt_idx = (virt >> 12) & 0x3FF; // Средние 10 бит — индекс таблицы + uint32_t pd_idx = virt >> 22; + uint32_t pt_idx = (virt >> 12) & 0x3FF; + uint32_t temp_pt_idx = (VMM_TEMP_MAP >> 12) & 0x3FF; + uint32_t* tmp = (uint32_t*)temp_table_phys; if (!(page_directory[pd_idx] & 1)) { - // 1. Выделяем физическую страницу под НОВУЮ таблицу - uint32_t phys_table = (uint32_t)pmm_alloc_page(); - - // 2. Ставим её в каталог (Present + Write) + uint32_t phys_table = (uint32_t)pmm_alloc_page(); + if (!phys_table) { + return; + } page_directory[pd_idx] = (phys_table & 0xFFFFF000) | 3; - - // 3. Очищаем новую таблицу (важно!) - uint32_t* new_table = (uint32_t*)(page_directory[pd_idx] & 0xFFFFF000); - for(int i=0; i<1024; i++) new_table[i] = 0; + + tmp[temp_pt_idx] = (phys_table & 0xFFFFF000) | 3; + __asm__ __volatile__("invlpg (%0)" : : "r"(VMM_TEMP_MAP) : "memory"); + + uint32_t* new_table = (uint32_t*)VMM_TEMP_MAP; + for (int i = 0; i < 1024; i++) new_table[i] = 0; + + new_table[pt_idx] = (phys & 0xFFFFF000) | flags; + + tmp[temp_pt_idx] = 0; + __asm__ __volatile__("invlpg (%0)" : : "r"(VMM_TEMP_MAP) : "memory"); + __asm__ __volatile__("invlpg (%0)" : : "r"(virt) : "memory"); + return; } - // Достаем адрес таблицы страниц из каталога (обнуляем нижние 12 бит флагов) - uint32_t* table = (uint32_t*)(page_directory[pd_idx] & 0xFFFFF000); + uint32_t table_phys = page_directory[pd_idx] & 0xFFFFF000; + tmp[temp_pt_idx] = table_phys | 3; + __asm__ __volatile__("invlpg (%0)" : : "r"(VMM_TEMP_MAP) : "memory"); - // Запихиваем физический адрес и флаги в таблицу страниц - // PAGE_SIZE у нас 4096, поэтому phys должен быть выровнен (нижние 12 бит затрутся флагами) + uint32_t* table = (uint32_t*)VMM_TEMP_MAP; table[pt_idx] = (phys & 0xFFFFF000) | flags; - // ВАЖНО: Процессор кэширует таблицы в TLB (Translation Lookaside Buffer). - // Если мы изменили маппинг существующей страницы, процессор об этом не узнает. - // Нужно сбросить кэш для этого адреса инструкцией invlpg: + tmp[temp_pt_idx] = 0; + __asm__ __volatile__("invlpg (%0)" : : "r"(VMM_TEMP_MAP) : "memory"); __asm__ __volatile__("invlpg (%0)" : : "r"(virt) : "memory"); } - diff --git a/src/vmm.h b/src/vmm.h index 9270791..5dc36a2 100644 --- a/src/vmm.h +++ b/src/vmm.h @@ -8,6 +8,9 @@ #define PAGE_WRITE 0x2 // Можно писать (Read/Write) #define PAGE_USER 0x4 // Доступно из Ring 3 (User mode) +// Временный маппинг для доступа к физической памяти (pd_idx=1023) +#define VMM_TEMP_MAP 0xFFC00000 + // Инициализация страничного обмена void vmm_init(void);