MULTIPLE: 3 изменения
PIC: добавил ремаппер IRQ: добавил таймер KERNEL: работа со строками и памятью
This commit is contained in:
parent
884b58f304
commit
d64856e1b1
11 changed files with 371 additions and 11 deletions
7
Makefile
7
Makefile
|
|
@ -1,5 +1,5 @@
|
|||
CC = gcc
|
||||
CFLAGS = -m32 -ffreestanding -O2 -Wall -Wextra -Iinclude
|
||||
CFLAGS = -m32 -ffreestanding -O2 -Wall -Wextra -Iinclude -fno-stack-protector
|
||||
|
||||
AS = nasm
|
||||
ASFLAGS = -f elf32
|
||||
|
|
@ -19,8 +19,11 @@ OBJ = boot/boot.o \
|
|||
kernel/interrupt.o \
|
||||
kernel/logger.o \
|
||||
kernel/io.o \
|
||||
kernel/pic.o \
|
||||
kernel/string.o \
|
||||
drivers/serial.o \
|
||||
drivers/vga.o
|
||||
drivers/vga.o \
|
||||
drivers/timer.o
|
||||
|
||||
# Дефолтное правило (просто сборка бинарника)
|
||||
all: $(TARGET)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
- [x] Драйвер VGA
|
||||
- [x] Прерывания (GDT/IDT)
|
||||
- [x] UART отладка
|
||||
- [ ] Драйвер таймера
|
||||
- [x] Драйвер таймера
|
||||
- [ ] Драйвер клавиатуры
|
||||
- [ ] Нормальный драйвер VGA (скролл итд)
|
||||
- [ ] Нормальный драйвер клавиатуры (Caps Lock, Shift, Numpad итд)
|
||||
|
|
|
|||
31
drivers/timer.c
Normal file
31
drivers/timer.c
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#include "timer.h"
|
||||
#include "../kernel/io.h"
|
||||
#include "../kernel/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) {
|
||||
system_ticks++;
|
||||
log_info(LOG_DETAIL, "Timer: tick!");
|
||||
// Каждые 100 тиков (1 раз в секунду) шлем инфу в логгер
|
||||
if (system_ticks % 100 == 0) {
|
||||
log_info(LOG_DETAIL, "Timer: one second passed");
|
||||
}
|
||||
}
|
||||
12
drivers/timer.h
Normal file
12
drivers/timer.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Инициализация таймера нужной частотой (например, 100 Гц)
|
||||
void timer_init(uint32_t frequency);
|
||||
|
||||
// Эта функция будет вызываться из нашего ассемблерного обработчика irq0
|
||||
void timer_handler(void);
|
||||
|
||||
#endif
|
||||
14
kernel/idt.c
14
kernel/idt.c
|
|
@ -1,4 +1,7 @@
|
|||
#include "idt.h"
|
||||
#include "string.h"
|
||||
#include "../drivers/serial.h"
|
||||
#include "../drivers/timer.h"
|
||||
|
||||
// Сам массив IDT на 256 векторов и указатель на него
|
||||
struct idt_entry idt[256];
|
||||
|
|
@ -71,12 +74,19 @@ void isr_handler(struct registers regs) {
|
|||
|
||||
// Сюда сходятся все прерывания от железа (32-47)
|
||||
void irq_handler(struct registers regs) {
|
||||
if (regs.int_no == 32) {
|
||||
timer_handler();
|
||||
} else {
|
||||
char irq_str[4];
|
||||
itoa(regs.int_no, irq_str, 10);
|
||||
kprint_serial("got unknown IRQ ");
|
||||
kprint_serial(irq_str);
|
||||
kprint_serial("\n");
|
||||
}
|
||||
// Если прерывание пришло от чипа PIC2 (IRQ 8-15, векторы 40-47), шлем ему EOI (End of Interrupt)
|
||||
if (regs.int_no >= 40) {
|
||||
__asm__ __volatile__("outb %%al, %%dx" :: "a"(0x20), "d"(0xA0));
|
||||
}
|
||||
// В любом случае шлем EOI главному чипу PIC1 (порты 0x20/0x20)
|
||||
__asm__ __volatile__("outb %%al, %%dx" :: "a"(0x20), "d"(0x20));
|
||||
|
||||
// Сюда в будущем добавим вызовы конкретных драйверов таймера и клавы
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include "../drivers/vga.h"
|
||||
#include "../drivers/serial.h"
|
||||
#include "../drivers/timer.h"
|
||||
#include "gdt.h"
|
||||
#include "idt.h"
|
||||
#include "pic.h"
|
||||
#include "logger.h"
|
||||
|
||||
void kernel_main(void) {
|
||||
|
|
@ -15,5 +17,13 @@ void kernel_main(void) {
|
|||
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, "Registering timer at 100Hz");
|
||||
timer_init(100);
|
||||
log_info(LOG_NORMAL, "Enabling IRQ...");
|
||||
__asm__ __volatile__("sti");
|
||||
while (1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,17 +9,11 @@ void logger_init(void) {
|
|||
|
||||
void klog(log_level_t level, const char* prefix, const char* message) {
|
||||
// 1. Форматируем и отправляем в Serial в любом случае
|
||||
kprint_serial("[");
|
||||
kprint_serial(prefix);
|
||||
kprint_serial("] ");
|
||||
kprint_serial(message);
|
||||
kprint_serial("\n");
|
||||
|
||||
// 2. Если уровень LOG_NORMAL, дублируем вывод на экран VGA
|
||||
if (level == LOG_NORMAL) {
|
||||
vga_print("[");
|
||||
vga_print(prefix);
|
||||
vga_print("] ");
|
||||
vga_print(message);
|
||||
vga_print("\n");
|
||||
}
|
||||
|
|
|
|||
76
kernel/pic.c
Normal file
76
kernel/pic.c
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#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
|
||||
|
||||
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)
|
||||
io_wait();
|
||||
outb(PIC2_DATA, slave_offset); // Slave IRQ 8-15 -> вешаем на slave_offset (40)
|
||||
io_wait();
|
||||
|
||||
// Связываем Master и Slave между собой (ICW3)
|
||||
outb(PIC1_DATA, 4); // Сообщаем Master, что Slave сидит на IRQ2 (00000100б = 4)
|
||||
io_wait();
|
||||
outb(PIC2_DATA, 2); // Сообщаем Slave его каскадный ID (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) {
|
||||
port = PIC1_DATA;
|
||||
} else {
|
||||
port = PIC2_DATA;
|
||||
irq_line -= 8;
|
||||
}
|
||||
uint8_t value = inb(port) | (1 << irq_line);
|
||||
outb(port, value);
|
||||
}
|
||||
|
||||
// Разрешить прерывание от конкретного IRQ
|
||||
void pic_clear_mask(uint8_t irq_line) {
|
||||
uint16_t port;
|
||||
if (irq_line < 8) {
|
||||
port = PIC1_DATA;
|
||||
} else {
|
||||
port = PIC2_DATA;
|
||||
irq_line -= 8;
|
||||
}
|
||||
uint8_t value = inb(port) & ~(1 << irq_line);
|
||||
outb(port, value);
|
||||
}
|
||||
13
kernel/pic.h
Normal file
13
kernel/pic.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef PIC_H
|
||||
#define PIC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Перемапливание базовых векторов прерываний для Master и Slave PIC
|
||||
void pic_remap(uint8_t master_offset, uint8_t slave_offset);
|
||||
|
||||
// Маскирование/размаскирование конкретной линии IRQ
|
||||
void pic_set_mask(uint8_t irq_line);
|
||||
void pic_clear_mask(uint8_t irq_line);
|
||||
|
||||
#endif
|
||||
183
kernel/string.c
Normal file
183
kernel/string.c
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
#include "string.h"
|
||||
|
||||
// Заполнить кусок памяти определенным байтом (очень нужно для очистки буферов)
|
||||
void* memset(void* dest, int val, size_t count) {
|
||||
uint8_t* temp = (uint8_t*)dest;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
temp[i] = (uint8_t)val;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Скопировать кусок памяти (будем использовать для скроллинга VGA экрана)
|
||||
void* memcpy(void* dest, const void* src, size_t count) {
|
||||
uint8_t* d = (uint8_t*)dest;
|
||||
const uint8_t* s = (const uint8_t*)src;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Скопировать кусок памяти с учетом возможного пересечения областей
|
||||
void* memmove(void* dest, const void* src, size_t count) {
|
||||
uint8_t* d = (uint8_t*)dest;
|
||||
const uint8_t* s = (const uint8_t*)src;
|
||||
|
||||
if (d < s) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
} else {
|
||||
for (size_t i = count; i > 0; i--) {
|
||||
d[i - 1] = s[i - 1];
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Сравнение блоков памяти
|
||||
int memcmp(const void* s1, const void* s2, size_t count) {
|
||||
const uint8_t* p1 = (const uint8_t*)s1;
|
||||
const uint8_t* p2 = (const uint8_t*)s2;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (p1[i] != p2[i]) {
|
||||
return p1[i] - p2[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Длина строки
|
||||
size_t strlen(const char* str) {
|
||||
size_t len = 0;
|
||||
while (str[len] != '\0') {
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// Копирование строки
|
||||
char* strcpy(char* dest, const char* src) {
|
||||
size_t i = 0;
|
||||
while ((dest[i] = src[i]) != '\0') {
|
||||
i++;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Конкатенация строк
|
||||
char* strcat(char* dest, const char* src) {
|
||||
size_t dest_len = strlen(dest);
|
||||
size_t i = 0;
|
||||
while ((dest[dest_len + i] = src[i]) != '\0') {
|
||||
i++;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Сравнение строк (понадобится, когда будем парсить команды в терминале)
|
||||
int strcmp(const char* s1, const char* s2) {
|
||||
while (*s1 && (*s1 == *s2)) {
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
|
||||
}
|
||||
|
||||
// Строка в число
|
||||
int atoi(const char* str) {
|
||||
int res = 0;
|
||||
int sign = 1;
|
||||
int i = 0;
|
||||
|
||||
// Пропускаем пробелы, если они есть
|
||||
while (str[i] == ' ' || str[i] == '\t') {
|
||||
i++;
|
||||
}
|
||||
|
||||
// Учитываем знак
|
||||
if (str[i] == '-') {
|
||||
sign = -1;
|
||||
i++;
|
||||
} else if (str[i] == '+') {
|
||||
i++;
|
||||
}
|
||||
|
||||
// Считаем само число
|
||||
while (str[i] >= '0' && str[i] <= '9') {
|
||||
res = res * 10 + (str[i] - '0');
|
||||
i++;
|
||||
}
|
||||
|
||||
return sign * res;
|
||||
}
|
||||
|
||||
// Число в строку (поддерживает base = 10 и base = 2)
|
||||
char* itoa(int value, char* str, int base) {
|
||||
int i = 0;
|
||||
int is_negative = 0;
|
||||
|
||||
// Защита от дурака
|
||||
if (base < 2 || base > 36) {
|
||||
str[0] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
// Обрабатываем 0
|
||||
if (value == 0) {
|
||||
str[i++] = '0';
|
||||
str[i] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
// Отрицательные числа обрабатываем только для десятичной системы
|
||||
if (value < 0 && base == 10) {
|
||||
is_negative = 1;
|
||||
value = -value;
|
||||
}
|
||||
|
||||
// Переводим число в строку (но она получится задом наперед)
|
||||
while (value != 0) {
|
||||
int rem = value % base;
|
||||
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
|
||||
value = value / base;
|
||||
}
|
||||
|
||||
// Добавляем минус
|
||||
if (is_negative) {
|
||||
str[i++] = '-';
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
|
||||
// Переворачиваем строку в нормальный вид
|
||||
int start = 0;
|
||||
int end = i - 1;
|
||||
while (start < end) {
|
||||
char temp = str[start];
|
||||
str[start] = str[end];
|
||||
str[end] = temp;
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// Число в красивый Hex вид (например, 255 -> "0x000000ff")
|
||||
char* itox(uint32_t value, char* str) {
|
||||
char* hex_digits = "0123456789abcdef";
|
||||
|
||||
str[0] = '0';
|
||||
str[1] = 'x';
|
||||
str[10] = '\0'; // 2 символа на "0x" + 8 символов на 32-битное число
|
||||
|
||||
// Заполняем с конца
|
||||
for (int i = 9; i >= 2; i--) {
|
||||
str[i] = hex_digits[value & 0xF];
|
||||
value >>= 4;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
28
kernel/string.h
Normal file
28
kernel/string.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef STRING_H
|
||||
#define STRING_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Работа с памятью
|
||||
void* memset(void* dest, int val, size_t count);
|
||||
void* memcpy(void* dest, const void* src, size_t count);
|
||||
void* memmove(void* dest, const void* src, size_t count);
|
||||
int memcmp(const void* s1, const void* s2, size_t count);
|
||||
|
||||
// Работа со строками
|
||||
size_t strlen(const char* str);
|
||||
char* strcpy(char* dest, const char* src);
|
||||
char* strcat(char* dest, const char* src);
|
||||
int strcmp(const char* s1, const char* s2);
|
||||
|
||||
// Перевод строки в число
|
||||
int atoi(const char* str);
|
||||
|
||||
// Перевод числа в строку (десятичную)
|
||||
char* itoa(int value, char* str, int base);
|
||||
|
||||
// Специальный быстрый аналог для Hex (16-ричной системы), незаменим для дампов памяти
|
||||
char* itox(uint32_t value, char* str);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue