在MINIX中,中断处理程序是用宏来定义的,那么我们就来学习一下他的做法:
extern IRQ_Table
...
%macro HW_INT_MASTER 1
call Save
mov al,20h
out 20h,al
in al,21h
or al,(1 << %1)
out 21h,al
sti
mov eax,0dh
push eax
push Int_Msg
call Disp_Color_Str
add esp,8
call [IRQ_Table + (%1 * 4)]
cli
in al,21h
and al,~(1 << %1)
out 21h,al
ret
%endmacro
%macro HW_INT_SLAVE 1
push %1;
call IRQ_Handler
add esp,4
hlt
%endmacro
HW_Int_00:
HW_INT_MASTER 0
HW_Int_01:
HW_INT_MASTER 1
HW_Int_02:
HW_INT_MASTER 2
HW_Int_03:
HW_INT_MASTER 3
HW_Int_04:
HW_INT_MASTER 4
HW_Int_05:
HW_INT_MASTER 5
HW_Int_06:
HW_INT_MASTER 6
HW_Int_07:
HW_INT_MASTER 7
HW_Int_08:
HW_INT_SLAVE 8
HW_Int_09:
HW_INT_SLAVE 9
HW_Int_10:
HW_INT_SLAVE 10
HW_Int_11:
HW_INT_SLAVE 11
HW_Int_12:
HW_INT_SLAVE 12
HW_Int_13:
HW_INT_SLAVE 13
HW_Int_14:
HW_INT_SLAVE 14
HW_Int_15:
HW_INT_SLAVE 15
我们看到,多了一个IRQ_Table,这是函数指针数组,用来放置外中断的处理函数指针,它的类型是IRQ_Handler,在type.h中声明:
- typedef void (*IRQ_Handler)(u32);
IRQ_Table分别在global.h和global.c中声明和定义:
- extern IRQ_Handler IRQ_Table[]; /* in global.h */
- IRQ_Handler IRQ_Table[IRQ_NUM]; /* in global.c */
IRQ_NUM在protect.h中定义:
- #define IRQ_NUM 16
那么接下来就来填充IRQ_Table,除了时钟中断外都填充为IRQ_Handler函数指针,哦,该死,发现重名了,那么把函数名改成Spurious_IRQ吧,此函数在i8259.c中:
- void Spurious_IRQ(u32 irq_no)
- {
- Disp_Color_Str("IRQ_NO:",0xd);
- Disp_Int(irq_no);
- Disp_Color_Str("/n",0xd);
- }
相应的,在proto.h中也得改改:
- void Spurious_IRQ(u32 irq_no);
又发现了一个BUG,即Spurious_IRQ和Clock_Handler函数的类型不同,那么就把Clock_Handler改为:
- void Clock_Handler(u32 irq_no)
- {
- if(Is_Reenter != 0)
- {
- Disp_Color_Str("!",0xe);
- return;
- }
- p_Resume_PCB++;
- if(p_Resume_PCB >= PCB_Tables + 2)
- {
- p_Resume_PCB = PCB_Tables;
- }
- }
同样别忘了在proto.h中修改一下:
- void Clock_Handler(u32 irq_no);
下面就来填充这个数组吧,在Init_8259A函数中填充吧,先都填充为Spurious_IRQ,等会再填充特定的入口函数:
- int i;
- for(i = 0 ; i < IRQ_NUM ; i++)
- {
- IRQ_Table[i] = Spurious_IRQ;
- }
需要包含一些头文件:
- #include "protect.h"
- #include "proc.h"
- #include "global.h"
别忘了在HW_INT_SLAVE宏中把IRQ_Handler改为Spurious_IRQ,同时把extern IRQ_Handler改为Spurious_IRQ。
接下来是完成在IRQ_Table中填充特定的入口函数的功能了,先在i8259.c中写成一个函数:
- void Put_IRQ_Handler(u32 irq_no,IRQ_Handler handler)
- {
- Disable_IRQ(irq_no);
- IRQ_Table[irq_no] = handler;
- }
Disable_IRQ和等会要写的Enable_IRQ是用汇编来写的,放在kliba.asm中,目的是屏蔽和激活一个指定的中断:
global Disable_IRQ
global Enable_IRQ
...
;Disable_IRQ==========================================================
Disable_IRQ:
push ebp
mov ebp,esp
push eax
push ecx
pushf
mov ecx,[ebp + 8]
cli
mov ah,1
rol ah,cl
cmp cl,8
jae disable_8
disable_0:
in al,21h
test al,ah
jnz dis_already
or al,ah
out 0a0h,al
popf
pop ecx
pop eax
pop ebp
ret
disable_8:
in al,0a1h
test al,ah
jnz dis_already
or al,ah
out 0a1h,al
popf
pop ecx
pop eax
pop ebp
ret
dis_already:
popf
pop ecx
pop eax
pop ebp
ret
;end of Disable_IRQ===================================================
;Enable_IRQ===========================================================
Enable_IRQ:
push ebp
mov ebp,esp
push eax
push ecx
pushf
mov ecx,[ebp + 8]
cli
mov ah,1
not ah
rol ah,cl
cmp cl,8
jae enable_8
enable_0:
in al,21h
and al,ah
out 21h,al
popf
pop ecx
pop eax
pop ebp
ret
enable_8:
in al,0a1h
and al,ah
out 21h,al
popf
pop ecx
pop eax
pop ebp
ret
;end of Enable_IRQ====================================================
别忘了在proto.h添加相关函数的声明:
- void Put_IRQ_Handler(u32 irq_no,IRQ_Handler handler);
- void Disable_IRQ(u32 irq_no);
- void Enable(u32 irq_no);
最后在第一个进程开始执行前用Put_IRQ_Handler初始化一下,就在Init_PCB中初始化吧:
- Put_IRQ_Handler(0,Clock_Handler);
- Enable_IRQ(0);
既然在这里已经激活了时钟中断,那么在Init_8259A函数中就应该屏蔽所有中断:
Out_Byte(0x21,0xff);
OK,到此为止所有的代码都已经修改添加完毕,编译运行,画面依然如故,但是我们的程序已经有了良好的扩展性,虽然还很幼稚,很多东西如进程优先级,进程间调度等都没有考虑到。虽然看起来很简单,但要想一口气从头写到尾,还是不太可能的。今天到此为止,休息,休息。