汇编语言中断

一、中断的概念#

  1. 中断与中断源

由于某种事件的发生,使得 CPU 暂时停止(中断)正在执行的程序,转而去执行处理该事件的程序,对该事件的处理结束后,再继续执行先前被中断的程序,这个过程称为中断。引起中断的事件称为中断源,它们可能是来自外设的 I/O 请求,也可能是计算机的一些异常事故或其他内部原因,还有可能是为调试程序而设置的中断源等。

  1. 中断源的分类

80x86 的中断源如图 3 所示,根据中断源所处的位置,可分为外部中断源和内部中断源。

80x86 中断源

图 3 80x86 中断源

(1)内部中断源来自 CPU 的内部,其特点是不需要外部硬件支持,不受中断允许标志 IF 的限制,内部中断也称为软件中断。通常由以下三种情况引起。

① 中断指令 INT。

中断指令 INT 必须指定一个类型号,如执行 INT 21H 指令时,CPU 会立刻产生中断,从 84H 存储单元(84H=21H×4)的开始位置取出两个字分别送 IP 和 CS,从而实现类型号为 21H 的系统功能调用。

② CPU 的某些错误。

CPU 的某些错误引发的中断,如除法出错(除数为 0 或商超出寄存器表示范围),则立刻产生类型为 0 的中断。

③ 为调试程序 Debug 而设置的中断。

单步中断是当标志位 TF=1 时,每条指令执行后,CPU 自动产生类型号为 1 的单步中断,单步中断处理程序的功能是执行一条用户指令后就停下,并把 TF 置 1,使 CPU 为单步方式。

断点中断是使程序从指定的位置开始执行,并暂停在某个指定的位置(断点)。设置断点实际上就是把 INT 3 指令插入到断点处,CPU 执行到断点处的 INT 3 指令就会产生类型号为 3 的断点中断。

(2)外部中断源来自 CPU 的外部。外部中断也称为硬件中断。有两条外部中断请求线:NMI(不可屏蔽中断源)和 INTR(可屏蔽中断源)。

① 不可屏蔽中断源由硬件故障引起,不受标志位 IF 的影响,在当前指令执行完以后,CPU 就响应。NMI 有电源掉电、存储器出错或总线奇偶检验错等,这些错误如不及时响应和处理,机器就难以操作下去,所以系统必须无条件地及时处理。

② 所谓可屏蔽中断源,就是允许程序员决定对该中断源是否予以处理。这由两个控制条件决定,一是该设备的中断请求是否被屏蔽掉,如果被屏蔽掉,则该设备就不能发出中断请求,当然更谈不上 CPU 的响应;二是 CPU 是否一定要响应未被屏蔽的中断请求呢?不是的,这要取决于标志寄存器 FLAGS 的中断允许位 IF 的状态,当 CPU 处于开中断状态(IF=1)时,CPU 能够响应外设的中断请求;当 CPU 处于关中断状态(IF=0)时,CPU 不响应外设的请求。可用 STI 指令开中断(使 IF=1),也可用 CLI 指令关中断(使 IF=0)。

可屏蔽中断源由 8259A 可编程中断控制器统一管理。通过对 8259A 可编程中断控制器中的中断屏蔽寄存器(IMR)的设置可以控制外设是否被屏蔽。中断屏蔽寄存器的 I/O 端口地址为 21H,它的 8 位对应控制 8 个外设,某位为 1 表示某种外设被屏蔽。例如,只允许键盘和定时器中断,可在主程序的初始化部分设置如下的中断屏蔽字(参见图 4 的中断屏蔽寄存器):

MOV  AL,11111100B

OUT  21H,AL

在一次中断处理完毕,退出中断处理程序之前,应该对 8259A 可编程中断控制器中的中断命令寄存器(端口地址为 20H)发中断结束命令 EOI(End of Interrupt)。中断结束命令的作用是使中断命令寄存器的 5 位(EOI)置 1,表示清除当前正在处理的中断请求。这样做的目的是通知 8259A,本次中断处理结束,让系统能够继续响应其他同级和低级的中断请求。

中断命令寄存器的 L2~L0 位指定 IR0~IR7 中最低优先级的中断,SL(Set Level)位和 R(rotate)位控制 IR0~IR7 的中断优先级顺序,它们的四种组合含义如下:

R  SL

0  0  正常优先级

0  1  清除由 L2~L0 位指定的中断请求

1  0  各中断优先级依次左循环一个位置

1  1  各中断优先级依次循环,使得 L2~L0 位指定的中断请求到达最低优先级位置

结束中断的指令为:

MOV  AL,00100111B

OUT  20H,AL

其中中断命令寄存器 EOI 位=1,表示结束当前中断;

L2~L0 位=111,表示最低优先级中断为 IR7;

SL 位和 R 位=00,表示正常优先级,正常优先级的次序为:IR0,IR1,…,IR7。

中断屏蔽寄存器和中断命令寄存器

图 4 中断屏蔽寄存器和中断命令寄存器

  1. 中断类型号

在实际的系统中,中断源有多个,需要给每个中断源编一个号,以便于识别。在执行软件中断指令 INT N 时,N 就是中断类型号。由于 CPU 引脚的限制,只有一条中断请求线连接 8259A 可编程中断控制器,当 8259A 控制器所连接的外部设备请求中断时,8259A 控制器向 CPU 发出 INTR 信号,于是 CPU 读取那个请求中断的设备的中断类型号,然后转去调用该设备的中断处理程序。在图 4 中,定时器的中断类型号为 08H。

  1. 中断优先级和中断嵌套

当在同一时刻有若干个中断源发出中断请求时,CPU 如何处理呢?CPU 应该按中断源的优先级顺序予以响应。这个优先级规则由程序员编程决定,交由 8259A 管理。

另外,正在运行的中断服务程序在开中断(IF=1)的情况下,可以被其他更高级的中断源中断,这种一个中断服务程序又被另一个中断服务程序中断的情况称为中断嵌套。因此需要对系统中的所有中断源设置中断优先级。系统规定的中断优先级从高到低的顺序依次为:

  • 内中断(除法错,INTO,INT);

  • 不可屏蔽中断(NMI);

  • 可屏蔽中断(INTR);

  • 单步中断(调试程序)。

① 对于可屏蔽中断(INTR),程序员可以通过对 8259A 可编程中断控制器编程,设置和改变中断优先级。

② 可屏蔽中断(INTR)的优先级分8级,默认情况的优先级次序为:

IR0,IR1,IR2,IR3,IR4,IR5,IR6,IR7

因此,在图 4 中,定时器的优先级最高。

二、中断向量表#

  1. 中断向量表

CPU 获得中断类型号后,把中断类型号自动乘以 4 计算出存放该中断处理程序的起始地址的存储器地址,从而从该地址取出该中断处理程序的起始地址,转去执行相应的中断处理程序(或称为中断服务程序)。我们把中断处理程序的起始地址称为中断向量。在存储器的最低 1KB(地址从 0000~3FFH)集中存放 256 种中断类型的中断向量,每个中断向量为四个字节,其中前两个字节是偏移地址,后两个字节是段地址。这个集中存放中断向量的存储区称为中断向量表。中断向量表如图 5 所示。256 个中断向量对应的中断类型号为 0~255(00000H~000FFH)。

中断向量表

图 5 中断向量表

无论是软件中断还是硬件中断,CPU 获得中断类型号以后,就从中断向量表中取出中断向量,送 IP 和 CS 寄存器,即调用相应的中断处理程序。

对于软件中断指令 INT N 来说,其操作如下。

(1)将标志寄存器内容、当前 CS 及 IP 的值入栈,并将 IF 和 TF 两个标志清零。

(2)取中断类型号 N,并计算中断向量地址为 4N。从中断向量地址表中 4N 处取中断向量,第一个字为偏移地址送 IP,第二个字为段地址送 CS,从而执行 N 号中断服务程序。

对于硬件中断,即便有程序在运行,在每一条指令的周期内,CPU 都会检测中断控制器,一旦捕捉到中断请求,即刻获取中断类型号。

由于采用中断类型号和中断向量表,使得计算机快速得到中断向量,转而执行相应的中断处理程序,从而大大提高了响应和处理速度。表 2 列出各类中断在中断向量表中的地址分配。

中断向量表的地址分配

表 2 中断向量表的地址分配

  1. 设置中断向量的方法

用户可以扩充自己的中断,有两种方法。

第一种方法比较直观,根据中断类型号和中断向量地址的关系,即中断类型号 ×4=中断向量地址,用指令直接设置:

MOV  AX,0

MOV  ES,AX

MOV  BX,N*4

MOV  AX,OFFSET INTMY     ; INTMY的偏移地址

MOV  ES: WORD PTR[BX],AX   ; 送中断向量表

MOV  AX,SEG INTMY      ; INTMY的段地址

MOV  ES: WORD PTR[BX+2],AX  ; 送中断向量表

…

INTMY:

…

IRET

程序中从 INTMY 标号开始到 IRET 指令这一段指令序列就是用户自己的中断处理程序,接下来,程序中就可以使用 INT N 指令调用中断类型号为 N 的中断处理程序。

第二种方法比较方便,通过 DOS 系统功能调用来完成。下面列出设置中断向量和取中断向量的 DOS 系统功能调用。

设置中断向量的步骤:

置:AH=25H

AL=中断类型号

DS:DX=中断向量

执行:INT 21H

取中断向量的步骤:

置:AH=35H

AL=中断类型号

执行:INT 21H

返回得到:ES:BX=中断向量

如果用户使用自己的中断处理程序替代系统中的某个中断处理程序,应注意在设置自己的中断向量前,先从中断向量表中取出原中断向量并保存,在中断处理程序执行结束前再恢复设置原中断向量。

例 2 使用 DOS 系统功能调用,取原中断向量并保存,再设置自己的中断向量,用完后再恢复原中断向量。程序段如下:

MOV  AL,N          ; 中断类型号N

MOV  AH,35H

INT  21H           ; 取中断向量到ES:BX

PUSH ES

PUSH BX           ; 保存中断向量

PUSH DS           ; 暂存当前DS

MOV  AX,SEG INTMY     ; INTMY的段地址

MOV  DS,AX

MOV  DX,OFFSET INTMY    ; INTMY的偏移地址

MOV  AL,N          ; 中断类型号N

MOV  AH,25H

INT  21H           ; 设置自己的中断向量

POP  DS           ; 恢复当前DS

…

;因为INTMY的中断向量已设置好,所以在这里,使用INT N指令可以调用INTMY中断处理程序。如果此时发生N号外部中断请求,也将启动执行INTMY中断处理程序

…

;下面这段代码是把原中断向量写入中断向量表

pop  dx           ; DX=原中断向量的偏移地址

POP  DS           ; DS=原中断向量的段地址

MOV  AL,N          ; 中断类型号N

MOV  AH,25H

INT  21H           ; 原中断向量写入中断向量表

RET

;下面这段代码是INTMY中断处理程序

INTMY:            ; INTMY中断处理程序的开始

…

IRET

三、中断过程#

从中断请求的发生到处理中断并从中断返回到原来程序被中断的位置,一个完整的中断过程一般经历以下五个步骤:中断请求、中断优先级判定、中断响应、中断处理和中断返回。

(1)中断请求。由中断源提出中断请求,如定时器时间到发出的中断请求、输入设备要求输入数据的中断请求等。

(2)中断优先级判定。按规定的优先级次序对各中断源进行判优。通常由硬件完成。

(3)中断响应。CPU 在每执行完一条指令后,通过硬件自动查询是否有中断请求,所以对中断请求能够及时发现。如果允许 CPU 响应(标志寄存器 FLAGS 的中断允许位 IF=1),则 CPU 自动完成以下工作:

① 取中断类型号 N;

② 标志奇存器入栈;

③ 代码段寄存器(CS)和指令指针(IP)入栈;

④ 禁止硬件中断和单步中断(IF=0,TF=0);

⑤ 在中断向量表中的 N×4 开始的单元取两个字分别送 IP 和 CS,即调用中断处理程序。

(4)中断处理。执行中断处理程序,程序一开始可以根据需要开中断,以允许中断嵌套。用入栈指令把中断处理程序中将要用到的寄存器内容压入堆栈,以保护现场,待中断处理完毕,退出中断处理程序之前把寄存器的内容从堆栈中弹出,从而恢复现场。

(5)中断返回。在中断处理程序的最后,用 EOI 指令清除本次中断,表示本次中断处理完毕,系统可以接受其他中断。如果此时没有其他中断请求,接着用 IRET 指令实现 IP 内容出栈,CS 内容出栈,状态寄存器的内容出栈,继续执行被中断了的程序。

四、中断调用指令#

在前面介绍中,我们已经使用过 INT 21H 指令来实现 DOS 系统功能调用。INT 中断调用指令和 CALL 子程序调用指令作用类似,也是转去执行子程序。INT 指令是特殊的子程序调用指令,调用的子程序即为中断服务程序。

前面已经介绍,计算机系统中的某些临时紧急事件发生时,通常采取中断措施,调用中断服务程序,程序的入口地址叫中断向量。为了能使用户也能在程序中调用这些中断服务程序,80x86 提供了 INT 中断指令。例如执行 INT 21H 指令时,机器把中断类型号 21H×4,即从地址为 84H 的中断向量区中取出对应的中断向量送给 CS 和 IP,从而实现 DOS 系统功能调用。

这里介绍 2 条中断命令:中断指令和中断返回指令。

(1)INT(interrupt)中断指令

格式:INT N

操作:PUSH FLAGS

PUSH CS

PUSH IP

IP←(N×4)

CS←(N×4+2)

其中 N 为中断类型号,它可以是常数或常数表达式,其值必须在 0~255 范围内。如指令中不给出N,它隐含的类型号为 3。INT 指令不影响除 IF、TF 和 AC 以外的标志位。

INT 指令是段间的间接调用,比 CALL 指令多了一个把标志寄存器推入堆栈的操作。

(2)IRET(return from interrupt)从中断返回指令

格式:IRET

操作:POP IP

POP CS

POP FLAGS

显然是恢复 INT 指令所保存的断点地址。

(完)

comments powered by Disqus