Я довольно новичок в разработке ОС, и недавно я начал хобби-проект создания простой текстовой операционной системы. Он написан на C с некоторой помощью сборки и использует GRUB для загрузки, и я тестировал его в VirtualBox, а также иногда помещал его на флэш-диск для тестирования на древнем (~ 2009) ноутбуке. До сих пор я реализовал некоторые базовые функции вывода текста, и я думаю, что мои реализации GDT и IDT в порядке, учитывая отсутствие сбоев в последнее время. В настоящее время я пытаюсь заставить работать драйвер клавиатуры, управляемый прерываниями.
Я думаю, что я правильно установил PIC, и, кажется, мне повезло, давая команды контроллеру PS / 2 и клавиатуре и получая ответы через обработчик прерываний. Например, вот выходные данные отладки, когда вы даете клавиатуре команду идентификации:
Initializing kernel...
Setting PS/2 controller status: 0x05
Sending keyboard command: 0xF2
Keyboard interrupt: 0xFA
Keyboard interrupt: 0xAB
Keyboard interrupt: 0x83
Кажется, что возвращенные данные верны, и это доказывает, что мой обработчик прерываний может работать несколько раз подряд без сбоев или чего-либо еще, поэтому я не слишком беспокоюсь о своей реализации IDT или ISR. Теперь вот вывод, когда я посылаю команду 0xF4 на клавиатуру, чтобы начать сканирование на нажатия клавиш:
Initializing kernel...
Setting PS/2 controller status: 0x05
Sending keyboard command: 0xF4
Keyboard interrupt: 0xFA
Прерывание с кодом подтверждения «0xFA» кажется многообещающим, но после этого ничего не происходит, когда я нажимаю клавиши. Для обоих примеров я получил одинаковые результаты при работе как в VirtualBox, так и на ноутбуке, который я использовал.
Вот соответствующий код из драйвера клавиатуры:
#define KEYBD_DATA 0x60
#define KEYBD_CMD 0x64
// wrapper for interrupt service routine written in assembly
extern void keyboard_interrupt();
// called from assembly ISR
void keyboard_handler() {
u8 data = read_port(KEYBD_DATA);
print("Keyboard interrupt: 0x");
printx(data);
putc('\n');
pic_eoi();
}
// functions to print command before sending it to the port
void keyboard_command(u8 cmd) {
print("Sending keyboard command: 0x");
printx(cmd);
putc('\n');
write_port(KEYBD_DATA, cmd);
}
void controller_command(u8 cmd) {
print("Sending controller command: 0x");
printx(cmd);
putc('\n');
write_port(KEYBD_CMD, cmd);
}
void setup_keyboard() {
// flush keyboard output
while(read_port(KEYBD_CMD) & 1)
read_port(KEYBD_DATA);
// set interrupt descriptor table entry (default code segment and access flags)
set_idt_entry(0x21, &keyboard_interrupt);
// activate device
write_port(KEYBD_CMD, 0xAE);
wait();
// get status
write_port(KEYBD_CMD, 0x20);
wait();
u8 status = (read_port(KEYBD_DATA) | 1) & 0x05;
print("Setting PS/2 controller status: 0x");
printx(status);
putc('\n');
wait();
// set status
write_port(KEYBD_CMD, 0x60);
wait();
write_port(KEYBD_DATA, status);
wait();
// enable keyboard scanning
keyboard_command(0xf4);
}
Не то чтобы я думаю, что это корень проблемы, но на всякий случай вот сборочная часть обработчика прерываний (в сборке GNU):
.extern keyboard_handler
.global keyboard_interrupt
keyboard_interrupt:
cli
pusha
cld
call keyboard_handler
popa
sti
iret
Вот код, который заранее устанавливает PIC:
#define MASTER_CMD 0x20
#define MASTER_DATA 0x21
#define SLAVE_CMD 0xA0
#define SLAVE_DATA 0xA1
#define PIC_EOI 0x20
// hopefully this gives a long enough delay
void wait() {
for (u8 i = 0; i < 255; i++);
}
// alert the PICs that the interrupt handling is done
// (later I'll check whether the slave PIC needs to be sent the EOI, but for now it doesn't seem to hurt to give it anyway)
void pic_eoi() {
write_port(MASTER_CMD, PIC_EOI);
write_port(SLAVE_CMD, PIC_EOI);
wait();
}
void setup_pic() {
write_port(MASTER_CMD, 0x11);
write_port(SLAVE_CMD, 0x11);
wait();
write_port(MASTER_DATA, 0x20);
write_port(SLAVE_DATA, 0x28);
wait();
write_port(MASTER_DATA, 0x4);
write_port(SLAVE_DATA, 0x2);
wait();
write_port(MASTER_DATA, 0x1);
write_port(SLAVE_DATA, 0x1);
wait();
write_port(MASTER_DATA, 0x0);
write_port(SLAVE_DATA, 0x0);
wait();
}
Вот порядок инициализации в основной части ядра:
// initialize global descriptor table and interrupt descriptor table
setup_gdt();
setup_idt();
// setup hardware interrupts
setup_pic();
setup_keyboard();
activate_idt(); // assembly routine with lidt and sti
Я также знаю, что клавиатура на самом деле делает свое дело и помещает коды сканирования на порт 0x60, и я смог использовать метод опроса, чтобы заставить работать нажатия клавиш, но он грязный и усложнит его работу. обрабатывать такие вещи, как повторение клавиш и отслеживание клавиши Shift. Дайте мне знать, если нужно больше кода. Надеюсь, есть что-то очевидное, что я либо забыл, либо делаю неправильно :) 10 *