汇编语言 第十二章 内中断

中断信息: CPU 在执行完当前正在执行的指令后,检测到从 CPU 外部发送过来的或内部产生的一种特殊信息,并立即处理。

12.1 内中断的产生

当 8086CPU 内部有下面的情况发生的时候,将会产生相应的中断信息。

  • 除法错误,比如,执行 div 指令产生的除法溢出;
  • 单步执行;
  • 执行 into 指令;
  • 执行 int 指令。 它们是不同的信息(中断源),CPU 要知道中断信息的来源。所以中断信息中必选包含识别来源的编码——中断类型码(字节型数据)。在 8086CPU 中的中断类型码如下:

  • 除法错误:0;
  • 单步执行:1;
  • 执行 into 指令:4;
  • 执行 int 指令,格式为 int n,指令中 n 为字节型立即数,即为中断类型码。

12.2 中断处理程序

CPU 收到中断信息后,需要对中断信息进行处理。我们编写的用来处理中断信息的程序被称为中断处理程序

12.3 中断向量表

CPU 用 8 位的中断类型码通过中断向量表找到对应的中断处理程序的入口地址。中断向量表就是中断处理程序入口地址的列表。

中断向量表在内存中存放,对于 8086PC 机,中断像量表指定放在内存地址 0 处。从内存 0000:0000 到 0000:03FF 的 1024 个单元中存放着中断向量表。8086CPU 规定必须在这,不在别处。

在中断向量表中,一个表项存放一个中断向量,即一个中断处理程序的入口地址。对于 8086CPU,这个入口地址包括段地址和偏移地址,所以一个表项占两个字,高地址存放段地址,低地址存放偏移地址。

12.4 中断过程

中断过程:用中断类型码在中断向量表中找到中断处理程序的入口。找到这个入口地址的最终目的是用它设置 CS 和 IP,使 CPU 执行中断处理程序。这个过程由 CPU 的硬件自动完成。

CPU 收到中断信息后,要处理中断信息,首先引发中断过程。硬件完成中断过程后,CS:IP 将指向中断处理程序的入口,CPU 开始执行中断处理程序。(原 CS:IP 的值需要保存,以便继续执行。)

下面是 8086CPU 在收到中断信息后,所引发的中断过程:

  1. (从中断信息中)取得中断类型码 N;
  2. 标志寄存器的值入栈;
  3. 设置标志寄存器的值的第 8 位 TF 和第 9 位 IF 的值为 0;
  4. CS 的内容入栈;
  5. IP 的内容入栈;
  6. 从内存地址为中断类型码*4 和中断类型码*4+2 的两个字单元中读取中断处理程序的入口地址设置 IP 和 CS。

最后一步完成后,CPU 开始执行由程序员编写的中断处理程序。

12.5 中断处理程序和 iret 指令

CPU 随时都可能执行中断处理程序,所以中断处理程序必须一直存放在内存某段空间之中。

中断处理程序的常规编写步骤:

  1. 保存用到的寄存器;
  2. 处理中断;
  3. 恢复用到的寄存器;
  4. 用 iret 指令返回。
1
2
3
4
#iret 指令的功能用汇编描述:
pop IS
pop CS
popf

iret 通常和硬件自动我弄成的中断过程配合使用。与寄存器入栈顺序正好对应相反。iret 指令执行后,CPU 会带执行中断处理程序前的执行点继续执行程序。

12.6 除法错误中断的处理

当 CPU 执行 div 等除法指令时,若发生了除法溢出错误,将产生中断类型码为 0 的中断信息,CPU 将检测到这个信息,然后引发中断过程,转去执行 0 号中断所对应的中断处理程序。

12.7 编程处理 0 号中断

功能是在屏幕中间显示 “overflow!”,然后返回操作系统。

分析:

  1. 当发生除法溢出时,产生 0 号中断信息,从而引发中断过程。

    此时进行如下过程:

    1. 取得中断类型码 0;
    2. 标志寄存器入栈,TF、IF 设置为 0;
    3. CS、IP 入栈
    4. (IP)=(0*4),(CS)=(0*4+2)。
  2. 当中断 0 发生后,CPU 将转去执行中断处理程序。

    1. 相关处理;
    2. 向显示缓冲区送字符串 “overflow!”;
    3. 返回 DOS。

    称这段程序为:do0。

  3. 除法溢出随时都可能发生,do0 放在哪?

    找到一块别的程序不会直接用的内存区——前文提到的中断向量表存放的内存(因为大多是空着的)。

  4. 将 do0 放到内存后(0:200),则将此内存地址登记在中断向量表的对应表项中。

1
2
3
4
5
6
7
8
9
10
11
assume cs:code
code segment
start: do0 安装程序
设置中断向量表
mov ax,4c00h
int 21h
do0: 显示字符串“overflow!”
mov ax,4c00h
int 21h
code ends
end start

12.8 安装

可以使用 movsb 指令,将 do0 代码送入 0:200 处。程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assume cs:code
code segment
start: 设置 es:di 指向目的地址
设置 ds:si 指向源地址
设置 cx 为传输长度
设置传输方向为正
rep movsb

设置中断向量表

mov ax,4c00h
int 21h
do0: 显示字符串"overflow!"
mov ax,4c00h
int 21h
code ends
end start

可以利用编译器来计算 do0 的长度,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
asssume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do0
mov ax,0
mov es,ax
mov di,200h

mov cx,offset do0end-offset do0

cld
rep movsb

设置中断向量表

mov ax,4c00h
int 21h

do0: 显示字符串"overflow!"
mov ax,4c00h
int 21h
code ends
end start

“-” 是编译器识别运算符号,编译器用它来进行两个常数的减法。

12.9 do0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
asssume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do0
mov ax,0
mov es,ax
mov di,200h
mov cx,offset do0end-offset do0
cld
rep movsb
设置中断向量表

mov ax,4c00h
int 21h

do0: jmp short do0start
db "overflow!"

do0start: mov ax,cs
mov ds,ax
mov si,202h

mov ax,0b800h
mov es,ax
mov di,12*160+36*2

mov cx,9
s: mov al,[si]
mov es:[di],al
inc si
add di,2
loop s

mov ax,4c00h
int 21h
do0end:nop
code ends
end start

12.10 设置中断向量

1
2
3
4
mov ax,0
mov es,ax
mov word ptr es:[0*4],200h
mov word ptr es:[0*4+2],0

12.11 单步中断

基本上,CPU 在执行一条指令后,若检测到标志寄存器的 TF 位为 1,则产生单步中断,引发中断过程。单步中断的中断类型码为 1,它所引发的中断过程:

  1. 取得中断类型码 1;
  2. 标志寄存器入栈,TF、IF 设置为 0;
  3. CS、IP 入栈
  4. (IP)=(1*4),(CS)=(1*4+2)。

TF = 1 时,CPU 在执行完一条指令后将引发单步中断,转去执行中断处理程序。但中断处理程序也是一条条指令组成的,何如?

CPU 当然不会这么傻,在进入中断处理程序之前,设置 TF = 0.从而避免这种情况。

CPU 提供单步中断功能的原因就是,为单步跟踪程序的执行过程,提供了实现机制。

12.12 响应中断的特殊情况

某些情况下,CPU 执行完当前指令后,即便发生中断,也不会响应。

在执行完向 ss 寄存器传送数据的指令后,即便是发生中断,CPU 也不会响应。因为 ss:sp 联合指向栈顶,而对它们的设置应该连续完成。

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×