第13章 int 指令
中断信息可以来自CPU的内部和外部,当CPU的内部有需要处理的事情发生的时候,将产生马上需要处理的中断信息,引发中断过程。
这一章,我们要讲解另一种重要的内中断,由int指令引发的中断
13.1 int指令
int指令的格式为int n 指令,相当于引发一个n号中断的中断过程,执行情况如下:
- 取中断类型码n;
- 标志寄存器入栈,IF=0,TF=0;
- CS,IP入栈;
- 修改IP,CS,跳转到中断程序处理入口。
注意,我们前面所说的中断都是被动触发的,当满足某种错误的条件,就会触发相关错误指令,而int指令是主动触发的,这种区别一定要清楚。
int 指令在程序中的任何地方都可以被加载触发。
13.2 编写供应用程序调用的中断历程。
编程
要求:求一word型数据的平方
参数:(ax)=要计算的数据。
返回值:dx,ax中存放结果的高16位和低16位。
举例应用: 2*3456^2
assmue cs:code
code segment
start: mov ax,3456
add ax,ax
adc dx,dx
mov ax,4cooh
int 21
code ends
end start
分析一下,我们要做以下3部分工作。
- 编写实现求平方功能的程序。
- 安装程序,将其安装在0号内存出
- 设置中断向量表,将程序的入口地址保存在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,200g
mov cx,offset sqrend-offset sqr
cld
rep movsb
mov ax,0
mov es,0
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
mov int 21h
sqr: mul ax
iret
sqrend: nop
code ends
end start
注意,在执行程序结束后,应该调用iret:
pop IP
pop CS
pop f
功能是调出地址和标志寄存器的值。
程序2
功能:将一个全是字母,以0结尾的字符串,转换为大写。
参数:ds: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,4cooh
int 21h
code ends
end start
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset capital
mov ax,0
mov es,ax
mov di,200h
mov cx,offset capitalend- offset capital
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
capital: push cx
push si
change: mov cl,[si]
mov ch,0
jcxz ok
and byte ptr [si],11011111b
inc si
jmp short change
ok: pop si
pop cx
iret
captialend: nop
code ends
end start
注意:不能向段寄存器中加入单纯的数据,必须要使用相关寄存器。
mov ax,cs
mov ds,ax
可以直接向偏移地址中加入offset
的相关信息。
设置ds:si指向源地址
设置es:di指向目的地址
s结尾段地址,i结尾偏移地址
代码段寄存器 DS(Data Segment):
堆栈段寄存器 ES(Extra Segment);
SI(Source Index):源变址寄存器
DI(Destination Index):目的变址寄存器
这个对偏移地址有效,这种操作是你需要明确的。
13.3 对int,iret和栈的深入理解
通过使用int程序,可以停止当前,然后运行相关程序来修改原来的命令。
我们调用中断程序中,我们的CS:IP放入栈,而之后用iret来将取出栈中。
我们知道CS:IP来栈中的相对位置。
我们可以在中断程序中来修改栈中的CS:IP地址,之后当执行iret程序时,我们并不会回到原来的地址,而是回到我们修改过的地址。
这节就是讲了这点内容,然后就是一些相关例子代码,看明白即可。
编程 在屏幕中间显示80个‘!’
assume cs:code
code segment
start: mov,ax,0b800h
mov es,ax
mov di,160*12
mov bx,offset s-offset se
mov cx,80
s: mov byte ptr es:[di],'!'
add di,2
int 7ch
se: nop
mov ax,4c00h
int 21h
code ends
end start
可以看出,这里关于 7ch 的中断程序类似loop s
指令。
下面就是关于7ch的中断程序:
lp: push bp
mov dp,sp
dec cx
jcxz lpret
add [bp+2],bx
lpret: pop bp
iret
这里的dp寄存器是一个浮动的栈游标,空,bp,ip,cs
,而[bp+2],这个明显指向IP地址。
而bx在进入该程序前是se到s的相对地址,负数,然后加上就能往前移动。
如果cx==0,那么直接跳到退出的环节,而不是来修改栈中的IP地址,这样,程序就能很自然的进行下面的语句了。
13.4 BIOS和DOS所提供的中断例程
在系统版的ROM中存放着一套程序,被称为BIOS(基本输入输出系统),BIOS中主要包含以下几部分内容。
- 硬件系统的检测和初始化程序。
- 外部中断和内部中断历程。
- 用于对外部硬件设备进行I/O操作的中断历程。
- 其他和硬件相关的中断历程。
操作系统DOS也提供了中断历程,从操作系统的角度来看,DOS的中断历程就是操作性向程序猿提供的编程资源,可以供程序猿自由的发挥。
BIOS和DOS所提供的中断历程中包含了许多子程序,这些子程序实现了程序猿在编程的时候经常需要用到的工程。可以用int指令直接调用BIOS和DOS提供的中断历程,来完成某些工作。
13.5 BIOS和DOS中断例程的安装过程
前面我们编写的安装程序,我们可以放在内存中,别的程序在运行时都可以调用。
而BIOS和DOS提供的中断例程是如何安装到内存中的呢?
1. 开机后,CPU一加点,初始化(CS)=0FFFFH,(IP)=0,自动从0FFFFH:0单元开始执行程序。
0FFFF:0处有一条跳转指令,CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。
2. 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。注意,对于BIOS所提供的中断例程,只需要将入口登记在中断向量表中即可,因为它们是固化到ROM中的程序,一直在内存中存在,所以,不用我们自己编写和安装,这个道理要明确的。
3. 操作系统检测和初始化完成后,调用int 19h进行操作系统的引导。从此将计算机交由操作系统控制。
4. DOS启动后,除完成其他工作外,还要将所提供的中断例程装入内存,并建立相应的中断向量,这些是操作系统的,软件层间而非固化在ROM的硬件层面上。
13.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
ah=2,表示调用10h中的2号子程序,功能为重制光标的位置。
bh中页号的含义:内存地址空间中,B800H~BFFFH共32kb的空间,为80*25彩色字符模式的显示缓冲区。一屏的内容在显示缓冲区中共占4000个字节。
显示缓冲区分8页,每页4KB,显示器可以显示任意一页的内容。一般情况下,显示第0页的内容。
在看看int 10h中段例程的在光标位置显示字符的功能
mov ah,9 ;在光标处显示字符
mov al,'a' ;字符
mov bl,7 ;颜色属性
mov bh,0 ;第0页
mov cx,3 ;字符重复个数
int 10h
两个程序结合可以在屏幕中间显示字符,先把光标放到中间,之后显示字符,就是这样,很简单。