汇编语言(七)--中断程序设计

本文最后更新于:2019年6月26日 晚上

概览:中断的概念、分类,CPU如何得知中断,中断的过程,中断向量表,自定义中断

中断的概念和分类

中断就是指CPU在执行指令的过程中,收到了中断请求,于是终止正在执行的程序去处理中断,处理完之后返回被终止的程序继续执行。

中断分为两大类:软件中断(内中断)与硬件中断(外中断)

软件中断

  1. 中断指令 int n
    ·比如DOS功能 INT 21H

  2. CPU遇到了某些运算错误
    除法错中断(中断类型号 0):执行除法指令时,若发现除数为0或超过了寄存器所能表达的范围,则立即产生一个类型为0的中断。(自己写的div 0之后运行就会卡住)
    溢出中断(中断类型号 4):若溢出标志OF置1,由中断指令INTO来处理发生溢出的中断操作;若OF为0,则INTO指令不产生中断,CPU继续运行原程序。

  3. debug程序时设置的中断
    单步中断(中断类型号 1):在DEBUG下执行T命令(或P命令)时,陷阱标志TF置为1,CPU自动产生类型为1的单步中断。产生单步中断时,CPU自动地将PSW、CS和IP的内容入栈保存,然后清除TF、IF。
    断点中断(中断类型号 3):断点中断也是供DEBUG调试程序使用的。在调试程序时,当CPU执行到断点时便产生中断(G命令),这时显示出各寄存器及相关标志,可以查看寄存器或存储单元的内容。设置断点实际上是把一条断点指令INT 3插入到程序中,CPU每执行到断点处的INT 3指令,便产生一个中断。

硬件中断

硬件中断是由输入/输出外设发出中断请求引起的中断。

  1. 不可屏蔽中断
    通常是遇到故障时,比如电源故障、奇偶检验错、I/O通道校验错等紧急情况时系统自动产生的。
    这些不可屏蔽中断请求信号会接到CPU的NMI引脚上,不可屏蔽中断的中断类型号为2。
    NMI (Non Maskable Interrupt)——不可屏蔽中断(即CPU不能屏蔽)无论状态寄存器中 IF 位的状态如何,CPU收到有效的NMI必须进行响应。

  2. 可屏蔽中断
    可屏蔽中断是键盘、显示器、打印机、磁盘、串行口/并行口等外设发出的。由于可屏蔽中断种类较多,各种处理要求不一样,因此系统专门用8259中断控制器来管理这些中断。
    可屏蔽是指可以使用软件设置允许或者禁止CPU进行响应。因为除了本身是否发出中断请求之外,该请求是否执行还要看CPU的IF标志的控制。

不可屏蔽中断CPU必须立刻响应,而可屏蔽中断可以选择响应或者不响应,或者按照优先级排序处理。

典型的非屏蔽中断源的例子是电源掉电,一旦出现,必须立即无条件地响应,否则进行其他任何工作都是没有意义的。

典型的可屏蔽中断源的例子是打印机中断,CPU对打印机中断请求的响应可以快一些,也可以慢一些,因为让打印机等待儿是完全可以的。

CPU处理中断

80X86系统中所有的中断请求都会有与之相对应的中断处理子程序。CPU响应中断之后会跳转到中断处理子程序,执行完之后会回到原来程序执行的断点处继续执行。

中断类型

80X86系统一共提供了256个中断类型,类型号为0–FFH。

但是系统只使用了一部分类型号。

中断向量与中断向量表

CPU要跳转执行中断处理子程序就需要知道它存放在那里,有入口地址才能够跳转执行。这个入口地址称之为中断向量(段地址+偏移地址)。

中断向量表,80X86系统建立的保存中断向量的地址区域,位于内存最低地址区中0号单元开始的1KB单元之中。

一个中断向量占用2个字即四个字节,高字单元根据放段地址,低字单元放偏移地址。

由于从0号单元开始,根据中断类型号码,乘以四就可以得到内存向量。

中断向量表

中断优先级

内部中断优先级最高,其次是非屏蔽中断,优先级最低的是单步中断。

中断优先级

CPU中断过程

  1. 中断响应条件
    (1)  当前的指令周期结束;
    (2)  采样到有效的中断请求信号;
    (3)  如果是可屏蔽中断请求INTR,检查中断允许标志IF是否为1,即中断开放;
    (4) CPU正在执行的程序不是中断服务程序,或者是中断优先级较低的中断服务程序。
    此外,特殊情况CPU不会响应中断。
    (1) 当执行到STI指令时,CPU不会马上响应中断。STI指令是开中断指令,要求在开放中断后再执行后续的一条指令后才能响应中断;
    (2) IRET指令是中断子程序返回指令,它也要求再执行一条后续指令后才能响应中断。这样做的目的是保护系统能够正常运行;
    (3)当执行MOV SS,AX指令,即向SS段寄存器传送数据时,即使发生了中断,CPU也不会响应;直到本条执行完后,接着再执行一条指令才响应中断。
    所以遇到非屏蔽中断时,这些特殊情况怎么做???

  2. 中断响应过程
    (1)首先将标志寄存器FLAGS压入堆栈,将陷阱标志TF存入暂存器;
    (2)将IF和TF清零;
    (3)将正在运行程序的断点的CS和IP压入堆栈;
    (4)从中断向量表中取出中断向量高两个字节的内容送入CS,取出低两个字节的内容送到IP;
    (5)转到相应中断源的中断服务程序入口,执行中断处理服务程序。
    简单来说:就是先保护现场,然后更换CS:IP的内容,执行。

  3. 中断处理
    即执行中断处理子程序。
    中断之中可以嵌套中断,如果新的中断优先级高于现在处理的中断,CPU就会转去执行新的中断。

  4. 中断返回
    中断子程序的结尾为IRET。即中断返回,恢复现场。
    将保存在堆栈中断点的偏移地址和段地址弹出,修改IP和CS寄存器;再把保存在堆栈中的PSW各标志位弹到FLAGS寄存器中,然后返回到被中断的程序去继续执行。

在中断发生时,CPU自动清除了IF位和TF位。使执行中断处理过程中,避免再次发生外部中断的干扰。

定制自己的中断程序

步骤

  1. 保护现场
  2. STI 开中断指令(IF=1)
  3. 处理中断
  4. CLI 关中断指令(IF=0)
  5. 恢复现场
  6. IRET 指令,返回被中断的程序

从应用程序进入中断子程序时,IF、TF都被清除,此时CPU在中断子程序中不会再响应其他的外部中断请求。

但如果这个中断子程序允许被打断或者优先级不高的话,设计中断子程序时就要开放中断标志,允许被打断。

IRET指令和RET指令都是返回原调用程序,但是IRET指令还要弹出保存的标志寄存器的值

中断的设置

  1. 设置中断向量
    DOS中断INT 21H的25H号功能。将DS:DX中的中断向量写入中断向量表。
    DS存储中断子程序名的段地址,DX存储中断子程序名的偏移地址。

    AH=25H
    AL=中断类型号码
    DS:DX=中断向量
    INT 21H

  2. 取出中断向量
    DOS中断INT 21H的35H号功能。从中断向量表中取出中断向量放入ES:BX。
    ES存储中断子程序名的段地址,BX存储中断子程序名的偏移地址。

    AH=35H
    AL=中断类型号码
    INT 21H

  3. 中断驻留
    特殊的退出程序,在退出前保留程序占用的内存,使得程序的内存不会被其他程序占据或覆盖,从而让此中断程序驻留在内存,从而其他程序也可以调用此中断。

    AH=31H
    AL=0;表示返回码
    DX=驻留程序的长度
    INT 21H

    附:驻留程序的长度
    中断子程序
    mail proc far ;中断子程序名
    smail: ;子程序开始标号
    …… ;子程序内容
    iret
    smaisl:nop ;标号的结尾,Nop表示无操作的意思。
    mail endp
    故驻留程序的长度为:DX=smaisl-smail+16。两个标号名字不同,最后记得+16.

案例:7ch号功能,显示一段字符

  ;7ch号中断处理程序的功能是在屏幕上输出自己的班级,班级序号,学号,姓名。
  ;例如:1706 1 20177004 xuhaoyong
  ;并在另外一个程序程序中调用7ch号中断。
  data segment
      mess db 0ah,0dh,'1701 5 20170000 colourso $'
      mess1 db 0ah,0dh,'Trigger interrupt, display information $'
      mess2 db 0ah,0dh,'Continue or Quit(c/q): $'
  data ends
  stack segment stack
      db 256 dup(?)
  stack ends
  code segment
      assume cs:code,ds:data
      start:
      ;mov ax,data
      ;mov ds,ax
  ;主程序
  main proc far
  mov ax,seg show
  mov ds,ax        ;将子程序的段地址给ds
  mov dx,offset show;偏移地址
  mov ah,25h
  mov al,7ch        ;中断类型号
  int 21h            ;写入中断向量表

  ;调用7ch号中断
  con:
  mov ax,data
  mov ds,ax
  mov dx,offset mess1
  mov ah,9
  int 21h    ;输出

  int 7ch

  mov dx,offset mess2
  mov ah,9
  int 21h    ;输出

  mov ah,1
  int 21h
  cmp al,'c'
  je con

  ;退出的话将子程序中断驻留
  mov ah,31h
  mov al,0
  mov dx,showend-showstart+16
  int 21h

  mov ah,4ch
  int 21h
  main endp

  ;中断子程序
  show proc far
  showstart:
      sti;开中断
      mov ax,data
  mov ds,ax
  mov dx,offset mess
  mov ah,9
  int 21h    ;输出

  cli    ;关中断
  iret
  showend:nop
  show endp
  code ends
      end start

向量表的位置:7ch *4h = 1f0h。故在0000:01f0处查看int 7ch的中断向量表。

INT 7CH中断