中断信息可以来自CPU的内部和外部, 当CPU的内部有需要处理的事情发生的时候,将产生需要马上处理的中断信息,引发中断过程。在http://t.csdn.cn/jihpG,我们讲解了中断过程和两种内中断的处理。
这一章中, 我们讲解另一种重要的内中断, 由int指令引发的中断。
int指令
int指令的格式为:int n
n为中断类型码, 它的功能是引发中断过程。
CPU执行int n指令, 相当于引发一个n号中断的中断过程, 执行过程如下:
(1)取中断类型码n;
(2)标志寄存器入栈,IF=0,TF=0;
(3)CS、IP入栈;
(4)(IP)=(n*4),(CS)=(n*4+2)
可以在程序中使用int指令调用任何一个中断的中断处理程序。例如, 下面的程序:
这个程序在DOS方式下执行时, 将在屏幕中间显示一个"!",然后显示"Divide overflow"后返回到系统中。"!"是我们编程显示的, 而"Divide overflow"是哪里来的呢?我们的程序中又没有做除法, 不可能产生除法溢出。
程序是没有做除法, 但是在结尾使用了int 0指令。CPU执行int 0指令时, 将引发中断过程, 执行0号中断处理程序, 而系统设置的0号中断处理程序的功能是显示"Divide overflow", 然后返回到系统。
一般情况下,系统将一些具有一定功能的子程序,以中断处理程序的方式提供给应用程序调用。我们在编程的时候, 可以用int指令调用这些子程序。当然, 也可以自己编写一些中断处理程序供别人使用。以后,我们可以将中断处理程序简称为中断例程。
编写供应用程序调用的中断例程
前面,我们已经编写过中断0的中断例程了,现在我们讨论可以供应用程序调用的中断例程的编写方法。下面通过两个问题来讨论。
问题一:编写、安装中断7ch的中断例程。
功能:求一word型数据的平方。
参数:(ax)=要计算的数据。
返回值:dx、ax中存放结果的高16位和低16位。
应用举例:求:2*3456^2。
分析一下,我们要做以下3部分工作。
(1)编写实现求平方功能的程序;
(2)安装程序,将其安装在0:200处;
(3)设置中断向量表,将程序的入口地址保存在7ch表项中,使其成为中断7ch的中断例程。
安装程序如下:
assume cs:code
code segment
start:
;安装sqr程序
mov ax,cs
mov ds,ax
mov si,offset sqr ;设置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址
mov cx,offset sqrend-offset sqr ;设置cx为传输长度
cld ;设置传输方向为正
rep movsb
;设置中断向量表
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
sqr:mul ax ;求平方
iret
sqrend:nop
code ends
end start
注意, 在中断例程sqr的最后, 要使用iret指令。
用汇编语法描述, iret指令的功:
pop IP
pop cs
popf
CPU执行int 7ch指令进入中断例程之前, 标志寄存器、当前的CS和IP被压入栈中, 在执行完中断例程后, 应该用iret指令恢复int 7ch执行前的标志寄存器和CS、IP的值,从而接着执行应用程序。
int指令和iret指令的配合使用与call指令和ret指令的配合使用具有相似的思路。
对int、iret和栈的深入理解
问题:用7ch中断例程完成loop指令的功能。
loop s的执行需要两个信息, 循环次数和到s的位移, 所以, 7ch中断例程要完成loop指令的功能, 也需要这两个信息作为参数。我们用cx存放循环次数, 用bx存放位移。
应用举例:在屏幕中间显示80个'!'
assume cs:code
code segment
start:
;
mov ax,0b800h
mov es,ax
mov di,160*12+0
mov bx,offset s-offset se ;计算从标号s到标号se的转移位移
mov cx,80
s:mov byte ptr es:[di], '!'
add di,2
int 7ch ;如果(cx)≠0,转移到标号s处,就是完成loop s的功能
se:nop
mov ax,4c00h
int 21h
code ends
end start
在上面的程序中, 用int 7ch调用7ch中断例程进行转移, 用bx传递转移的位移。
分析:为了模拟loop指令, 7ch中断例程应具备下面的功能:
(1)dec cx;
(2)如果(cx)≠0,转到标号s处执行,否则向下执行。
下面我们分析7ch中断例程如何实现到目的地址的转移。
(1)转到标号s显然应设(CS)=标号s的段地址,(IP)=标号s的偏移地址。
(2)那么,中断例程如何得到标号s的段地址和偏移地址呢?
int 7ch引发中断过程后, 进入7ch中断例程, 在中断过程中, 当前的标志寄存器、CS和IP都要压栈,此时压入的CS和IP中的内容,分别是调用程序的段地址(可以认为是标号s的段地址) 和int 7ch后一条指令的偏移地址(即标号se的偏移地址) 。
可见,在中断例程中,可以从栈里取得标号s的段地址和标号se的偏移地址,而用标号se的偏移地址加上bx中存放的转移位移就可以得到标号s的偏移地址。
(3)现在知道,可以从栈中直接和间接地得到标号s的段地址和偏移地址,那么如何用它们设置CS:IP呢?
可以利用iret指令, 我们将栈中的se的偏移地址加上bx中的转移位移, 则栈中的se 的偏移地址就变为了s的偏移地址。我们再使用iret指令, 用栈中的内容设置CS、IP, 从而实现转移到标号s处。
7ch中断例程如下。
lp:push bp
mov bp,sp
dec cx
jcxz lpret ;cx=0就转移
add[bp+2],bx ;产生中断时,pushf push cs push ip,进入中断程序后又push bp
;(bp+2)就是se标号ip
;(bp+2)加上位移bx就是s的偏移地址
;之后iret过程中pop ip获取的就是s的偏移地址
lpret:pop bp
iret
BIOS和DOS中断例程的安装过程
前面的课程中,我们都是自己编写中断例程,将它们放到安装程序中,然后运行安装程序,将它们安装到指定的内存区中。此后,别的应用程序才可以调用。而BIOS和DOS提供的中断例程是如何安装到内存中的呢?我们下面讲解它们的安装过程。
(1) 开机后, CPU一加电, 初始化(CS) =0FFFFH, (IP) =0,自动从FFFF:0单元开始执行程序。FFFF:0处有一条转跳指令, CPU执行该指令后, 转去执行BIOS中的硬件系统检测和初始化程序。
(2)初始化程序将建立BIOS所支持的中断向量, 即将BIOS提供的中断例程的入口地址登记在中断向量表中。注意, 对于BIOS所提供的中断例程, 只需将入口地址登记在中断向量表中即可, 因为它们是固化到ROM中的程序, 一直在内存中存在。
(3)硬件系统检测和初始化完成后, 调用int 19h进行操作系统的引导。从此将计算机交由操作系统控制。
(4) DOS启动后, 除完成其他工作外, 还将它所提供的中断例程装入内存, 并建立相应的中断向量。
BIOS中断例程应用
下面我们举几个例子, 来看一下BIOS中断例程的应用。
int 10h中断例程是BIOS提供的中断例程, 其中包含了多个和屏幕输出相关的子程序。
一般来说,一个供程序员调用的中断例程中往往包括多个子程序,中断例程内部用传递进来的参数来决定执行哪一个子程序。BIOS和DOS提供的中断例程, 都用ah来传递内部子程序的编号。
下面看一下int 10h中断例程的设置光标位置功能。
(ah)=2表示调用第10h号中断例程的2号子程序,功能为设置光标位置,可以提供光标所在的行号(80*25字符模式下:0~24)、列号(80*25字符模式下:0~79),和页号作为参数。
bh中页号的含义:内存地址空间中, B8000H~B FFFFH共32kB的空间, 为80*25彩色字符模式的显示缓冲区。一屏的内容在显示缓冲区中共占4000个字节。
显示缓冲区分为8页,每页4KB(~4000B),显示器可以显示任意一页的内容。一般情况下,显示第0页的内容。也就是说,通常情况下,B8000H~B8F9FH中的4000个字节的内容将出现在显示器上。
再看一下int10h中断例程的在光标位置显示字符功能 。
(ah)=9表示调用第10h号中断例程的9号子程序,功能为在光标位置显示字符,可以提供要显示的字符、颜色属性、页号、字符重复个数作为参数。
bl中的颜色属性的格式如下。
编程:在屏幕的5行12列显示3个红底高亮闪烁绿色的'a'。
注意,闪烁的效果必须在全屏DOS方式下才能看到。
DOS中断例程应用
int 21h中断例程是DOS提供的中断例程, 其中包含了DOS提供给程序员在编程时调用的子程序。
我们前面一直使用的是int 21h中断例程的4ch号功能, 即程序返回功能, 如下:
mov ah,4ch ;程序返回
mov al,0 ;返回值
int 21h
(ah)=4ch表示调用第21h号中断例程的4ch号子程序,功能为程序返回,可以提供返回值作为参数。我们前面使用这个功能的时候经常写做:
mov ax,4c00h
int 21h
我们看一下int 21h中断例程在光标位置显示字符串的功能:
ds:dx ;指向字符串,要显示的字符串需用"$"作为结束符
mov ah,9;功能号9,表示在光标位置显示字符串
int 21h
(ah)=9表示调用第21h号中断例程的9号子程序,功能为在光标位置显示字符串,可以提供要显示字符串的地址作为参数。
上述程序在屏幕的5行12列显示字符串"Welcome to masm!", 直到遇见""(""本身并不显示,只起到边界的作用)。
如果字符串比较长,遇到行尾,程序会自动转到下一行开头处继续显示;如果到了最后一行,还能自动上卷一行。