int指令
中断信息可以来自CPU的外部和内部,当CPU的内部有需要处理的事情发生的时候,将产生需要马上处理的中断信息,引发中断例程。这一章中,我们学习另一种重要的内中断,由int指令引发的中断。
1. int指令
int指令的格式为:int n,n为中断类型码,它的功能是引发中断。CPU执行int n指令,相当于引发了一个n号中断的中断过程,执行过程如下:
- 取中断类型码;
- 标志寄存器入栈,IF=0,TF=0;
- CS、IP入栈;
- (IP) = (nx4),(CS) = (nx4+2)。
从此处转去执行n号中断的处理过程。
可以在程序中使用int指令调用任何一个中断的中断处理程序。例如下面的程序:
assume cs:code
code segment
start: mov ax, 0b800H
mov es, ax
mov byte ptr es:[12*160+40*2], '!'
int 0
code ends
end start
这个程序会在屏幕中间显示一个"!",然后显示"Divide overflow"后返回到系统中。"!"是我们编程显示的,而"Divide overflow"是哪里来的呢?我们在结尾处使用了int 0指令。CPU执行int 0指令时,将引发中断过程,执行0号中断处理程序,而系统设置的0号中断处理程序是显示"Divide overflow",然后返回到系统。
可见,int指令的最终功能和call指令相似,都是调用一定功能的程序。
一般情况下,系统将一些具有一定功能的子程序,以中断处理程序的方式提供给应用程序调用。我们在编程的时候,可以用int指令调用这些子程序。当然,也可以自己编写一些中断处理程序供别人使用。
2. 编写供应用程序调用的中断例程
问题一:编写、安装中断7cH的中断例程。
功能:求一word型数据的平方。
参数:(ax) = 要计算的数据。
返回值:dx、ax中存放结果的高16位和低16位。
应用举例:求2x3456^2
assume cs:code
code segment
start: mov ax, 3456
int 7cH
add ax, ax
add dx, dx
mov ax, 4c00H
int 21H
code ends
end start
我们需要左以下3部分工作。
- 编写实现求平方功能的程序;
- 安装程序,将其安装在0:0200处;
- 设置中断向量表,将程序的入口地址保存在7ch表项中,使其成为中断7cH的中断例程。
assume cs:code
code segment
start: mov ax, cs
mov ds, ax
mov si, offset sqr
mov ax, 0
mov es, ax
mov di, 0200H
mov cx, 0ffset sqrend - offset sqr ;设置cx的传输长度
cld ;设置正向传输
rep movsb
mov ax, 0
mov es, ax
mov word ptr es:[7cH*4], 0200H
mov word ptr es:[7cH*4+2], 0
mov ax, 4c00H
int 21H
sqr: mul ax
iret
sqrend: nop
code ends
end start
注意:在中断例程的最后,要使用iret指令。因为CPU在执行中断例程前,将标志寄存器和CS、IP入栈,在执行完中断例程后,应该用iret指令恢复int 7cH执行前的标志寄存器和CS、IP值,从而接着执行应用程序。
int指令和iret指令的配合使用与call指令和ret指令的配合使用具有相似的思路。
问题二:编写、安装中断7cH的中断例程。
功能:将一个全是字母,以0结尾的字符串,转化为大写。
参数:di:si指向字符串的首地址。
应用举例:将data段中的字符串转化为大写。
assume cs:code
data segment
db 'conversation',0
data ends
code segment
start: mov ax, data
mov ds, ax
mov si, 0
int 7cH
mov ax, 4c00H
int 21H
code ends
end start
安装程序如下:
assume cs:code
code segment
start: mov ax, cs
mov ds, ax
mov si, offset up
mov ax, 0
mov es, ax
mov di, 0200H
mov cx, offset upend - offset up ;设置传送长度
cld ;正向传输
rep movsb
mov ax, 0
mov es, ax
mov word ptr es:[7cH*4], 0200H
mov word ptr es:[7cH*4+2], 0
mov ax, 4c00H
int 21H
up: cmp byte ptr [si], 0
je ok
and byte ptr [si], 11011111B
inc si
jmp short up
ok: iret
upend: nop
code ends
end start
这里的程序和之前的并无太大的区别。我在这使用了cmp指令而不是jcxz指令,如果像书上一样使用了jcxz指令,则需要额外的寄存器。当使用了额外的寄存器,需要和前面call指令调用的子程序一样,进入子程序之前保存需要使用的寄存器原本的值。
3. 对int、iret和栈的深入理解
4. BIOS和DOS所提供的中断例程
在系统板的ROM中存放着一套程序,称为BIOS,BIOS中主要包含以下几部分内容:
- 硬件系统的检测和初始化程序
- 外部中断和内部中断的中断例程;
- 用于对硬件设备进行I/O操作的中断例程;
- 其它和硬件系统相关的中断例程。
BIOS和DOS在所提供的中断例程中包含了许多子程序,这些子程序实现了程序员在编程的时候经常需要用到的功能。程序员在编程的时候,可以用int指令直接调用BIOS和DOS提供的中断例程,来完成某些工作。
5. BIOS和DOS中断例程的安装过程
- 开机后,CPU加电,初始化(CS) = 0FFFFH, (IP) = 0, 自动从FFFF:0单元开始执行程序。FFFF:0处有一条跳转指令,CPU执行该指令后,转去执行BIOS中的硬件检测和初始化程序。
- 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。注意,对于BIOS所提供的中断例程,只需将入口地址登记在中断向量表中即可,因为它们是固化到ROM中的程序,一直在内存中存在。
- 硬件系统检测和初始化完成后,调用int 19H进行操作系统的引导。从此将计算机交由操作系统控制。
- DOS启动后,除完成其它工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量。
6. BIOS中断例程应用
int 10H中断例程是BIOS提供的中断例程,其中包含了多个和屏幕输出相关的子程序。
一般来说,一个供程序员调用的中断例程中往往包含多个子程序,中断例程内部用传递进来的参数来决定执行哪一个子程序。BIOS和DOS提供的中断例程,都用ah来传递内部子程序的编号。
下面看一下int 10H中断例程的设置光标位置功能。
mov ah, 2 ;置光标
mov bh, 0 ;第0页
mov dh, 5 ;dh中放行号
mov dl, 12 ;dl中放列号
int 10H