Được rồi, sau một quãng nghỉ ngắn, chúng ta hãy tiếp tục với loạt bài hướng dẫn của mình. Nếu quay lại đoạn mã trước, chúng ta sẽ thấy ISR cho lỗi chia cho 0. Bây giờ chúng ta cần điền vào các ISR còn lại mà chúng ta đã thiết lập thông báo trước đó (32 ISR đầu tiên). Bây giờ chúng ta sẽ tiếp tục lập trình ngắt; chúng ta sẽ tạo ra các IRQ, còn được gọi là Yêu cầu Ngắt. Các IRQ này được tạo ra bởi các thiết bị phần cứng như bàn phím, chuột, máy in, v.v. Ban đầu, 8 IRQ đầu tiên được tự động ánh xạ tới các vị trí IDT từ 8 đến 15. Vì chúng ta đã sử dụng 32 IRQ đầu tiên cho các ngoại lệ, nên bây giờ chúng ta cần ánh xạ lại chúng. Chúng ta sẽ gán các IRQ từ 32 đến 45. Để làm điều này, trước tiên chúng ta phải ánh xạ lại các IRQ:
void ND::IRQ::Remap(int pic1, int pic2)
{
#define PIC1 0x20
#define PIC2 0xA0
#define ICW1 0x11
#define ICW4 0x01
/* send ICW1 */
ND::Ports::OutputB(PIC1, ICW1);
ND::Ports::OutputB(PIC2, ICW1);
/* send ICW2 */
ND::Ports::OutputB(PIC1 + 1, pic1); /* remap */
ND::Ports::OutputB(PIC2 + 1, pic2); /* pics */
/* send ICW3 */
ND::Ports::OutputB(PIC1 + 1, 4); /* IRQ2 -> connection to slave */
ND::Ports::OutputB(PIC2 + 1, 2);
/* send ICW4 */
ND::Ports::OutputB(PIC1 + 1, ICW4);
ND::Ports::OutputB(PIC2 + 1, ICW4);
/* disable all IRQs */
ND::Ports::OutputB(PIC1 + 1, 0xFF);
}
Bây giờ chúng ta tạo một hàm để cài đặt IRQs:
void ND::IRQ::Install()
{
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_BLACK);
ND::Screen::PutString("\nInstalling IRQ...");
ND::IRQ::Remap(0x20,0x28);
ND::IDT::SetGate(32,(unsigned)ND::IRQ::IRQ1,0x08,0x8E);
ND::IDT::SetGate(33,(unsigned)ND::IRQ::IRQ2,0x08,0x8E);
ND::IDT::SetGate(34,(unsigned)ND::IRQ::IRQ3,0x08,0x8E);
ND::IDT::SetGate(35,(unsigned)ND::IRQ::IRQ4,0x08,0x8E);
ND::IDT::SetGate(36,(unsigned)ND::IRQ::IRQ5,0x08,0x8E);
ND::IDT::SetGate(37,(unsigned)ND::IRQ::IRQ6,0x08,0x8E);
ND::IDT::SetGate(38,(unsigned)ND::IRQ::IRQ7,0x08,0x8E);
ND::IDT::SetGate(39,(unsigned)ND::IRQ::IRQ8,0x08,0x8E);
ND::IDT::SetGate(40,(unsigned)ND::IRQ::IRQ9,0x08,0x8E);
ND::IDT::SetGate(41,(unsigned)ND::IRQ::IRQ10,0x08,0x8E);
ND::IDT::SetGate(42,(unsigned)ND::IRQ::IRQ11,0x08,0x8E);
ND::IDT::SetGate(43,(unsigned)ND::IRQ::IRQ12,0x08,0x8E);
ND::IDT::SetGate(44,(unsigned)ND::IRQ::IRQ13,0x08,0x8E);
ND::IDT::SetGate(45,(unsigned)ND::IRQ::IRQ14,0x08,0x8E);
ND::IDT::SetGate(46,(unsigned)ND::IRQ::IRQ15,0x08,0x8E);
ND::IDT::SetGate(47,(unsigned)ND::IRQ::IRQ16,0x08,0x8E);
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_GREEN);
ND::Screen::PutString("done");
asm volatile("sti");
}
Câu nói của asm sti chúng tôi kích hoạt IRQs. Bây giờ chúng ta đi với một cái gì đó tương tự như ISR. Các chức năng của IRQ cơ bản:
void ND::IRQ::IRQ1()
{
asm volatile(
"cli \n"
"pushl 0\n"
"pushl 32\n"
"jmp ND_IRQ_Common"
);
}
Một phần chung (giống như ISR):
extern "C"
void ND_IRQ_Common()
{
asm volatile(
"pusha \n"
"push %ds\n"
"push %es\n"
"push %fs\n"
"push %gs\n"
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movl %esp, %eax \n"
"push %eax \n"
"movl $ND_IRQ_Handler, %eax \n"
"call *%eax \n"
"popl %eax \n"
"popl %ds \n"
"popl %es \n"
"popl %fs \n"
"popl %gs \n"
"popa \n"
"addl 8, %esp \n"
"iret \n"
);
}
Và một trình xử lý cơ bản:
extern "C"
void ND_IRQ_Handler(struct regs* r)
{
void (*handler)(struct regs *r);
if(r->int_no >= 40)
{
ND::Ports::OutputB(0xA0,0x20);
}
ND::Ports::OutputB(0x20,0x20);
}
Với điều này, chúng ta nên kích hoạt IRQ ngay cả khi họ vẫn không làm gì cả. Trong chương tiếp theo, chúng ta sẽ xem cách lấy dữ liệu từ các IRQ này như đồng hồ hoặc bàn phím.
Và với điều này kết thúc bài viết ngày hôm nay. Như bạn thấy bây giờ tôi viết ít thường xuyên hơn do các vấn đề khác. Mặc dù vậy tôi sẽ tiếp tục cho đến khi có một hệ điều hành hoàn thiện hơn