中断和动态时钟显示

  中断就是打断处理器当前的执行流程,去执行另外一些和当前工作不相干的指令,执行完之后,还可以返回到原来的程序流程继续执行。

外部硬件中断

  顾名思义,外部硬件中断,就是从处理器外面来的中断信号。当外部设备发生错误,或者有数据要传送(比如,从网络中接收到一个针对当前主机的数据包),或者处理器交给它的事情处理完了(比如,打印已经完成),它们都会拍一下处理器的肩膀,告诉它应当先把手头上的事情放一放,来临时处理一下。

  外部硬件中断是通过两个信号线引入处理器内部的。这两根线的名字叫NMIINTR.

1. 非屏蔽中断

  中断不会被阻断和屏蔽的,称为非屏蔽中断(Non Maskable Interrupt,NMI),产生中断的设备,称为中断源。在传统的兼容模式下,NMI的中断源通过一个与非门连接到处理器。处理器的NMI引脚是高电平有效的,而中断信号是低电平有效的。当不存在中断信号的时候,与非门的所有输入都是为高,因为处理器的NMI引脚为低电平,这意味着没有中断发生。

  当有任何一个非屏蔽中断产生时,与非门的输出都为高,Intel处理器规定,NMI中断信号由0跳变到1后,至少要维持4个以上的时钟周期才算有效,才能被识别。在实模式下,NMI被赋予了统一的中断号2,不会再细分。

2. 可屏蔽中断

  可屏蔽中断是通过 INTR 引脚进入处理器内部的,像 NMI 一样,不可能为每一个中断源都提供一个引脚。而且,处理器每次只能处理一个中断。在这种情况下,需要一个代理,来接受外部设备发出的中断信号。还有,多个设备同时发出中断请求的机率也是很高的,所以该代理的任务还包括对它们进行仲裁,以决定让它们中的哪一个优先向处理器提出服务请求。其中用的最多的就是中断代理就是8259芯片又叫可编程中断控制器(Programming Interrupt Controller,PIC)

  Intel处理器允许256个中断,中断信号的范围是0~255,8259负责提供其中的15个,但中断号并不固定。该中断控制芯片有自己的端口号,可以像访问其他外部设备一样用in和out指令来改变它的状态,包括引脚中的中断号。
在这里插入图片描述
  8259芯片是 级联(Cascade) 关系,每片中只有8个中断输入引脚,主片的代理输出INT直接送到处理器的INTR引脚,从片的INT输出送到主片的引脚2上。

  在 8259 芯片内部,有中断屏蔽寄存器(Interrupt Mask Register,IMR),这是个 8 位寄存器,对应着该芯片的 8 个中断输入引脚,对应的位是 0 还是 1,决定了从该引脚来的中断信号是否能够通过 8259 送往处理器(0 表示允许,1 表示阻断)。当外部设备通过某个引脚送来一个中断请求信号时,如果它没有被 IMR 阻断,那么,它可以被送往处理器。注意,8259芯片是可编程的,主片的端口号是 0x20 和 0x21,从片的端口号是 0xa0 和 0xa1,可以通过这些端口访问 8259 芯片,设置它的工作方式,包括 IMR 的内容。

  中断能否被处理要看处理器,处理器内部有一个标志位IF,这就是中断标志(InterruptFlag)。当 IF 为 0 时,所有从处理器 INTR 引脚来的中断信号都被忽略掉;当其为 1 时,处理器可以接受和响应中断。IF 标志位可以通过两条指令 cli 和 sti 来改变。这两条指令都没有操作数,cli(CLear Interrupt flag) 用于清除 IF 标志位,sti(SeT Interrupt flag) 用于置位 IF 标志。

  当中断发生频发时,8259 芯片会记住它们,并按一定的策略决定先为谁服务。总体上来说,中断的优先级和引脚是相关的,主片的 IR0 引脚优先级最高,IR7引脚最低,从片也是如此。当然,还要考虑到从片是级联在主片的 IR2 引脚上。

  最后,当一个中断事件正在处理时,如果来了一个优先级更高的中断事件时,允许暂时中止当前的中断处理,先为优先级较高的中断事件服务,这称为中断嵌套

实模式下的中断向量表

在这里插入图片描述
  在实模式下,处理器要求将它们的入口点集中存放到内存中从物理地址0x00000开始,到0x003ff结束,共1KB的空间内,这就是所谓的中断向量表(Interrupt Vector Table,IVT)

  • 保护断点的现场。首先要将标志寄存器 FLAGS 压栈,然后清除它的 IF 位和 TF 位。接着,再将当前的代码段寄存器 CS 和指令指针寄存器 IP 压栈。注意,由于 IF 标志被清除,在中断处理过程中,处理器将不再响应硬件中断。如果希望更高优先级的中断嵌套,可以在编写中断处理程序时,适时用 sti 指令开放中断。
  • 执行中断处理程序。由于处理器已经拿到了中断号,它将该号码乘以 4(毕竟每个中断在中断向量表中占 4 字节),就得到了该中断入口点在中断向量表中的偏移地址。接着,从表中依次取出中断程序的偏移地址和段地址,并分别传送到 IPCS
  • 返回到断点接着执行。所有中断处理程序的最后一条指令必须是中断返回指令 iret。这将导致处理器依次从堆栈中弹出(恢复)IPCS 和 FLAGS 的原始内容,于是转到主程序接着执行。

  和可屏蔽中断不同,NMI 发生时,处理器不会从外部获得中断号,它自动生成中断号码 2,其他处理过程和可屏蔽中断相同。

实时时钟,CMOS RAM 和 BDC编码

  在外围设备控制芯片ICH内部,集成了实时时钟电路(Real Time Clock,RTC)和两小块由互补金属氧化物(CMOS)材料组成的静态存储器(CMOS RAM)。实时时钟电路负责计时,而日期和时间的数值则存储在这块存储器中。实时时钟是全天候跳动的,即使是在你关闭了计算机的电源之后,原因在于它由主板上的一个小电池提供能量。它为整台计算机提供一个基准时间,为所有需要时间的软件和硬件服务。

  日期和时间信息是保存在 CMOS RAM 中的,通常有 128 字节,而日期和时间信息只占了一小部分容量,其他空间则用于保存整机的配置信息,比如各种硬件的类型和工作参数、开机密码和辅助存储设备的启动顺序等。这些参数的修改通常在 BIOS SETUP 开机程序中进行。

  RTC 芯片由一个振荡频率为 32.768kHz 的石英晶体振荡器(晶振)驱动,经分频后,用于对CMOS RAM 进行每秒一次的时间刷新。常规的日期和时间信息占据了 CMOS RAM 开始部分的 10 字节,有年、月、日和时、分、秒,报警的时、分、秒用于产生到时间报警中断,如果它们的内容为 0xC0~0xFF,则表示不使用报警功能。
在这里插入图片描述
  CMOS RAM的访问,需要通过两个端口来进行。0x70或者0x74是索引端口,用来指定CMOS RAM 内的单元。0x71或者0x75是数据端口,用来读写响应单元里的内容。举个例子,以下代码用于读取今天是星期几:

mov al,0x06
out 0x70,al
in al,0x71

  端口0x70的最高位(bit 7)是控制NMI中断的开关。当它为0时,允许NMI中断到达处理器,为1时,则阻断所有的NMI信号,其他7个bit,即0~6位,则实际上用于指定CMOS RAM单元的索引号。

  单元0x0a~0x0d不是普通的储存单元,而是4个索引寄存器(8位寄存器)的索引号,也是通过0x70和0x71访问的,这4个寄存器用于设定实时时钟电路的参数和工作状态。

  寄存器 A 和 B 用于对 RTC 的功能进行整体性的设置,它们都是 8 位的寄存器,可读可写,其各位的用途如表:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  寄存器 C 和 D 是标志寄存器,这些标志反映了 RTC 的工作状态,寄存器 C 是只读的,寄存器D 则可读可写,它们也都是 8 位寄存器,其各位的含义如表所示。特别是寄存器 C,因为 RTC 可以产生中断,当中断产生时,可以通过该寄存器来识别中断的原因。
在这里插入图片描述

内部中断内部

  内部中断发生在处理器,是由执行的指令引起的。内部中断不受标志寄存器IF位的影响,也不需要中断识别总线周期,它们的中断类型是固定的,可以立即转入相应的处理过程。

软中断

  软中断是由int指令引起的中断处理。这类中断也不需要中断识别总线周期,中断号在指令中给出。

  最有名的软中断是BIOS中断,之所以称为BIOS中断,是因为这些中断功能是在计算机加电之后,BIOS程序执行期间建立起来的,这些中断功能在加载和执行主引导扇区代码之前就可以使用了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  BIOS 可能会为一些简单的外围设备提供初始化代码和功能调用代码,并填写中断向量表,但也有一些 BIOS 中断是由外部设备接口自己建立的。

  首先,每个外部设备接口,包括各种板卡,如网卡、显卡、键盘接口电路、硬件控制器等,都有自己的只读存储器(Read Only Memory,ROM),类似于 BIOS 芯片,这些 ROM 中提供了它自己的功能调用例程,以及本设备的初始化代码。按照规范,前两个单元的内容是 0x550xAA,第三个单元是本 ROM 中以 512 字节为单位的代码长度;从第四个单元开始,就是实际的 ROM 代码。

  在计算机启动期间,BIOS 程序会以 2KB 为单位搜索内存地址 C0000~E0000 之间的区域。当它发现某个区域的头两个字节是 0x55 和 0xAA 时,那意味着该区域有 ROM 代码存在,是有效的。接着,它对该区域做累加和检查,看结果是否和第三个单元相符。如果相符,就从第四个单元进入。这时,处理器执行的是硬件自带的程序指令,这些指令初始化外部设备的相关寄存器和工作状态,最后,填写相关的中断向量表,使它们指向自带的中断处理过程。

代码清单

         ;代码清单9-1
         ;文件名:c09_1.asm
         ;文件说明:用户程序     

;===============================================================================

SECTION header vstart=0                     ;定义用户程序头部段 

    program_length  dd program_end          ;程序总长度[0x00]

    ;用户程序入口点

    code_entry      dw start                ;偏移地址[0x04]

                    dd section.code.start   ;段地址[0x06]   

    realloc_tbl_len dw (header_end-realloc_begin)/4

                                            ;段重定位表项个数[0x0a]    

    realloc_begin:

    ;段重定位表           

    code_segment    dd section.code.start   ;[0x0c]

    data_segment    dd section.data.start   ;[0x14]

    stack_segment   dd section.stack.start  ;[0x1c]    

header_end:                

;===============================================================================

SECTION code align=16 vstart=0           ;定义代码段(16字节对齐) 

new_int_0x70:

      push ax

      push bx

      push cx

      push dx

      push es
      

  .w0:                                    

      mov al,0x0a                        ;阻断NMI。当然,通常是不必要的

      or al,0x80                          

      out 0x70,al

      in al,0x71                         ;读寄存器A

      test al,0x80                       ;测试第7位UIP 

      jnz .w0                            ;以上代码对于更新周期结束中断来说 

                                         ;是不必要的 

      xor al,al

      or al,0x80

      out 0x70,al

      in al,0x71                         ;读RTC当前时间()

      push ax

      mov al,2

      or al,0x80

      out 0x70,al

      in al,0x71                         ;读RTC当前时间()

      push ax

      mov al,4

      or al,0x80

      out 0x70,al

      in al,0x71                         ;读RTC当前时间()

      push ax

      mov al,0x0c                        ;寄存器C的索引。且开放NMI 

      out 0x70,al

      in al,0x71                         ;读一下RTC的寄存器C,否则只发生一次中断
                                         ;此处不考虑闹钟和周期性中断的情况 

      mov ax,0xb800

      mov es,ax

      pop ax

      call bcd_to_ascii

      mov bx,12*160 + 36*2               ;从屏幕上的1236列开始显示

      mov [es:bx],ah

      mov [es:bx+2],al                   ;显示两位小时数字

      mov al,':'

      mov [es:bx+4],al                   ;显示分隔符':'

      not byte [es:bx+5]                 ;反转显示属性 

      pop ax

      call bcd_to_ascii

      mov [es:bx+6],ah

      mov [es:bx+8],al                   ;显示两位分钟数字

      mov al,':'

      mov [es:bx+10],al                  ;显示分隔符':'

      not byte [es:bx+11]                ;反转显示属性

      pop ax

      call bcd_to_ascii

      mov [es:bx+12],ah

      mov [es:bx+14],al                  ;显示两位小时数字

      
      mov al,0x20                        ;中断结束命令EOI 

      out 0xa0,al                        ;向从片发送 

      out 0x20,al                        ;向主片发送 



      pop es

      pop dx

      pop cx

      pop bx

      pop ax

      iret

;-------------------------------------------------------------------------------

bcd_to_ascii:                            ;BCD码转ASCII
                                         ;输入:AL=bcd码
                                         ;输出:AX=ascii
      mov ah,al                          ;分拆成两个数字 

      and al,0x0f                        ;仅保留低4位 

      add al,0x30                        ;转换成ASCII 


      shr ah,4                           ;逻辑右移4位 

      and ah,0x0f                        

      add ah,0x30

      ret

;-------------------------------------------------------------------------------

start:

      mov ax,[stack_segment]

      mov ss,ax

      mov sp,ss_pointer

      mov ax,[data_segment]

      mov ds,ax

     
      mov bx,init_msg                    ;显示初始信息 

      call put_string


      mov bx,inst_msg                    ;显示安装信息 

      call put_string

     
      mov al,0x70

      mov bl,4

      mul bl                             ;计算0x70号中断在IVT中的偏移

      mov bx,ax                          
      
      cli                                ;防止改动期间发生新的0x70号中断

      push es

      mov ax,0x0000

      mov es,ax

      mov word [es:bx],new_int_0x70      ;偏移地址。                                          

      mov word [es:bx+2],cs              ;段地址

      pop es

      mov al,0x0b                        ;RTC寄存器B

      or al,0x80                         ;阻断NMI 

      out 0x70,al

      mov al,0x12                        ;设置寄存器B,禁止周期性中断,开放更 

      out 0x71,al                        ;新结束后中断,BCD码,24小时制 

      mov al,0x0c

      out 0x70,al

      in al,0x71                         ;读RTC寄存器C,复位未决的中断状态

      in al,0xa1                         ;8259从片的IMR寄存器 

      and al,0xfe                        ;清除bit 0(此位连接RTC)

      out 0xa1,al                        ;写回此寄存器 

      sti                                ;重新开放中断 

      mov bx,done_msg                    ;显示安装完成信息 

      call put_string


      mov bx,tips_msg                    ;显示提示信息

      call put_string      

      mov cx,0xb800

      mov ds,cx

      mov byte [12*160 + 33*2],'@'       ;屏幕第12行,35.idle:

      hlt                                ;使CPU进入低功耗状态,直到用中断唤醒

      not byte [12*160 + 33*2+1]         ;反转显示属性 

      jmp .idle

;-------------------------------------------------------------------------------

put_string:                              ;显示串(0结尾);输入:DS:BX=串地址

         mov cl,[bx]

         or cl,cl                        ;cl=0 ?

         jz .exit                        ;是的,返回主程序 

         call put_char

         inc bx                          ;下一个字符 

         jmp put_string

   .exit:

         ret

;-------------------------------------------------------------------------------

put_char:                                ;显示一个字符

                                         ;输入:cl=字符ascii
         push ax

         push bx

         push cx

         push dx

         push ds

         push es

         ;以下取当前光标位置

         mov dx,0x3d4

         mov al,0x0e

         out dx,al

         mov dx,0x3d5

         in al,dx                        ;8位 

         mov ah,al


         mov dx,0x3d4

         mov al,0x0f

         out dx,al

         mov dx,0x3d5

         in al,dx                        ;8位 

         mov bx,ax                       ;BX=代表光标位置的16位数
         

         cmp cl,0x0d                     ;回车符?

         jnz .put_0a                     ;不是。看看是不是换行等字符 

         mov ax,bx                       ; 

         mov bl,80                       

         div bl

         mul bl

         mov bx,ax

         jmp .set_cursor

 .put_0a:

         cmp cl,0x0a                     ;换行符?

         jnz .put_other                  ;不是,那就正常显示字符 

         add bx,80

         jmp .roll_screen

 .put_other:                             ;正常显示字符

         mov ax,0xb800

         mov es,ax

         shl bx,1

         mov [es:bx],cl


         ;以下将光标位置推进一个字符

         shr bx,1

         add bx,1

 .roll_screen:

         cmp bx,2000                     ;光标超出屏幕?滚屏

         jl .set_cursor


         mov ax,0xb800

         mov ds,ax

         mov es,ax

         cld

         mov si,0xa0

         mov di,0x00

         mov cx,1920

         rep movsw

         mov bx,3840                     ;清除屏幕最底一行

         mov cx,80
 .cls:

         mov word[es:bx],0x0720

         add bx,2

         loop .cls

         mov bx,1920

 .set_cursor:

         mov dx,0x3d4

         mov al,0x0e

         out dx,al

         mov dx,0x3d5

         mov al,bh

         out dx,al

         mov dx,0x3d4

         mov al,0x0f

         out dx,al

         mov dx,0x3d5

         mov al,bl

         out dx,al
         

         pop es

         pop ds

         pop dx

         pop cx

         pop bx

         pop ax

         ret

;===============================================================================

SECTION data align=16 vstart=0

    init_msg       db 'Starting...',0x0d,0x0a,0                   

    inst_msg       db 'Installing a new interrupt 70H...',0    

    done_msg       db 'Done.',0x0d,0x0a,0

    tips_msg       db 'Clock is now working.',0
       
;===============================================================================

SECTION stack align=16 vstart=0
                 resb 256
ss_pointer:
;===============================================================================

SECTION program_trail

program_end:
         ;代码清单9-2

         ;文件名:c09_2.asm

         ;文件说明:用于演示BIOS中断的用户程序      

;===============================================================================

SECTION header vstart=0                     ;定义用户程序头部段 

    program_length  dd program_end          ;程序总长度[0x00]    

    ;用户程序入口点

    code_entry      dw start                ;偏移地址[0x04]

                    dd section.code.start   ;段地址[0x06] 

    realloc_tbl_len dw (header_end-realloc_begin)/4

                                            ;段重定位表项个数[0x0a]

    realloc_begin:

    ;段重定位表           

    code_segment    dd section.code.start   ;[0x0c]

    data_segment    dd section.data.start   ;[0x14]

    stack_segment   dd section.stack.start  ;[0x1c]    

header_end:                
   
;===============================================================================

SECTION code align=16 vstart=0           ;定义代码段(16字节对齐) 

start:

      mov ax,[stack_segment]

      mov ss,ax

      mov sp,ss_pointer

      mov ax,[data_segment]

      mov ds,ax

      mov cx,msg_end-message

      mov bx,message
     
 .putc:

      mov ah,0x0e

      mov al,[bx]

      int 0x10

      inc bx

      loop .putc

 .reps:

      mov ah,0x00

      int 0x16
      mov ah,0x0e

      mov bl,0x07

      int 0x10

      jmp .reps

;===============================================================================

SECTION data align=16 vstart=0

    message       db 'Hello, friend!',0x0d,0x0a

                  db 'This simple procedure used to demonstrate '

                  db 'the BIOS interrupt.',0x0d,0x0a

                  db 'Please press the keys on the keyboard ->'

    msg_end:
                 
;===============================================================================

SECTION stack align=16 vstart=0           

                 resb 256

ss_pointer:

;===============================================================================

SECTION program_trail

program_end:
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃米饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值