【第6章】模仿MINIX的中断处理【三】

     在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中声明:

Code:
  1. typedef void (*IRQ_Handler)(u32);  

      IRQ_Table分别在global.h和global.c中声明和定义:

Code:
  1. extern IRQ_Handler IRQ_Table[];  /* in global.h */  
Code:
  1. IRQ_Handler IRQ_Table[IRQ_NUM];  /* in global.c */  

      IRQ_NUM在protect.h中定义:

Code:
  1. #define IRQ_NUM                 16  

   那么接下来就来填充IRQ_Table,除了时钟中断外都填充为IRQ_Handler函数指针,哦,该死,发现重名了,那么把函数名改成Spurious_IRQ吧,此函数在i8259.c中:

Code:
  1. void Spurious_IRQ(u32 irq_no)  
  2. {  
  3.     Disp_Color_Str("IRQ_NO:",0xd);  
  4.     Disp_Int(irq_no);  
  5.     Disp_Color_Str("/n",0xd);  
  6. }  

   相应的,在proto.h中也得改改:

Code:
  1. void Spurious_IRQ(u32 irq_no);  

       又发现了一个BUG,即Spurious_IRQ和Clock_Handler函数的类型不同,那么就把Clock_Handler改为:

Code:
  1. void Clock_Handler(u32 irq_no)  
  2. {  
  3.     if(Is_Reenter != 0)  
  4.     {  
  5.         Disp_Color_Str("!",0xe);  
  6.         return;       
  7.     }  
  8.       
  9.     p_Resume_PCB++;  
  10.     if(p_Resume_PCB >= PCB_Tables + 2)  
  11.     {  
  12.         p_Resume_PCB = PCB_Tables;        
  13.     }  
  14. }  

     同样别忘了在proto.h中修改一下:

Code:
  1. void Clock_Handler(u32 irq_no);  

      下面就来填充这个数组吧,在Init_8259A函数中填充吧,先都填充为Spurious_IRQ,等会再填充特定的入口函数:

Code:
  1. int i;  
  2. for(i = 0 ; i < IRQ_NUM ; i++)  
  3.     {  
  4.         IRQ_Table[i] = Spurious_IRQ;  
  5.     }   

       需要包含一些头文件

Code:
  1. #include "protect.h"  
  2. #include "proc.h"  
  3. #include "global.h"  

        别忘了在HW_INT_SLAVE宏中把IRQ_Handler改为Spurious_IRQ,同时把extern IRQ_Handler改为Spurious_IRQ。

   接下来是完成在IRQ_Table中填充特定的入口函数的功能了,先在i8259.c中写成一个函数:

Code:
  1. void Put_IRQ_Handler(u32 irq_no,IRQ_Handler handler)  
  2. {  
  3.     Disable_IRQ(irq_no);  
  4.     IRQ_Table[irq_no] = handler;  
  5. }  

   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添加相关函数的声明:

Code:
  1. void Put_IRQ_Handler(u32 irq_no,IRQ_Handler handler);  
  2. void Disable_IRQ(u32 irq_no);  
  3. void Enable(u32 irq_no);  

   最后在第一个进程开始执行前用Put_IRQ_Handler初始化一下,就在Init_PCB中初始化吧:

Code:
  1. Put_IRQ_Handler(0,Clock_Handler);  
  2. Enable_IRQ(0);  

   既然在这里已经激活了时钟中断,那么在Init_8259A函数中就应该屏蔽所有中断:

 Out_Byte(0x21,0xff);

   OK,到此为止所有的代码都已经修改添加完毕,编译运行,画面依然如故,但是我们的程序已经有了良好的扩展性,虽然还很幼稚,很多东西如进程优先级,进程间调度等都没有考虑到。虽然看起来很简单,但要想一口气从头写到尾,还是不太可能的。今天到此为止,休息,休息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值