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)。
从此处转去执行n号中断的中断处理程序。
可以在程序中使用int指令调用任何一个中断的中断处理程序。
assume cs:code code segment start:mov ax,0b800h mov es,ax mov byte ptr es:[12*160+40*2],'!' int 0 /*这里调用0号内存中断*/ code ends end start
程序执行后将在屏幕中间显示一个“ ! ”,然后显示“ Divide overflow" 后返回到系统中。”!”是编程显示的,程序是有做除法,但是在结尾使用了int 0指令。CPU执行int 0指令时,将引发中断过程,执行0号中断处理程序,而系统设置的0号中断处理程序的功能是显示“Divide overflow", 然后返回到系统。
可见, int指令的最终功能和call指令相似,都是调用一段程序。
一般情况下,系统将一些具有一定功能的子程序,以中断处理程序的方式提供给应用程序调用。在编程的时候,可以用int 指令调用这些子程序。当然,也可以编写一些中断处理程序供别人使用。
编写供应用程序调用的中断例程
功能:求一word型数据的平方。
参数:(ax)=要计算的数据。
返回值:dx、ax中存放结果的高16位和低16位。
举例: 求2*3456^2
assume cs:code code segment start:mov ax,3456 #(ax)=3456 int 7ch #调用中断7ch的中断过程,计算ax中的数据的平方,计算结果存在ds(高16位),ax(低16位)中 add ax,ax #ds:ax中的值乘以2第一步,低16位相加,如果进位则CF=1 adc dx,dx #dx:ax中的值乘以2第二部,高16位相加并加上CF mov ax,4c00h int 21h code ends end start
写中断例程分三步:
(1) 编写实现求平方功能的程序;
(2) 安装程序,将其安装在0:200处;
(3) 设置中断向量表,将程序的入口地址保存在7ch表项中,使其成为中断7ch的中断例程。
assume cs :code code segment start: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 #设置ex为传输长度 cld #设置传输方向为正 rep movsb #从ds:si拷贝到es:di,每拷贝1个字节,cx-1 mov ax,0 mov es,ax mov word ptr es:[7ch*4],200h #设置7ch中断程序的偏移量 mov word ptr es:[7ch*4+2],0 #设置7ch中断程序的段地址 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 指令的配合使用具有相似的思路。
功能:将一个全是字母,以0结尾的字符串,转化为大写。
参数: ds:si指向字符串的首地址。
应用举例:将data 段中的字符串转化为大写。
assume cs:code data segment db 'conversation',0 data ends code segrnent 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 #把程序放入0:200中 start:mov ax,cs mov ds,ax mov si,offset capital mov ax,O mov es,ax mov di,200h mov cx,offset capitalend-offset capital cld rep movsb #设置7ch中断程序的入口cs:ip 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] #把ds:si的值放入cl中 mov ch,O #cx高16位重置为零 jcxz ok #如果cx为0则跳转,字符串遍历到最后一位是0 (db '',0) and byte ptr [si],11011111b #大写操作 inc si jmp short change ok:pop si pop CX iret capitalend: nop code ends end start
对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 mov bx,offset s-offset se #设置从标号se 到标号s 的转移位移 mov cx,80 s:mov byte ptr es:[di],'!' add di,2 int 7ch #如果(cx)!=0 ,转移到标号s处 se:nop mov ax,4c00h int 21h code ends end start
中断程序
lp:push bp #bp入栈保存 mov bp,sp #把sp指向的栈顶给bp dec cx #cx-1 jcxz lpret #如果cx=0则跳转 #栈中的情况为:栈顶处是bp原来的数值,下面是se的偏移地址,再下面是s的段地址, 再下面是标志寄存器的值 add [bp+2],bx #修改栈中IP指向的位置,bp中为栈顶的偏移地址,所以((ss)*l6+(bp)+2)处为se的偏移地址,将它加上bx中的转移位移就变为s的偏移地址。最后用iret出栈返回,CS:IP即从标号s处开始执行指令。 lpret:pop bp iret
BIOS和DOS所提供的中断例程
在系统板的ROM中存放着一套程序,称为BIOS(基本输入输出系统),BIOS中主要包含以下几部分内容。
(1) 硬件系统的检测和初始化程序;
(2) 外部中断和内部中断的中断例程;
(3) 用于对硬件设备进行I/O操作的中断例程;
(4) 其他和硬件系统相关的中断例程。
操作系统DOS也提供了中断例程,从操作系统的角度来看,DOS的中断例程就是操作系统向程序员提供的编程资源。
BIOS和DOS在所提供的中断例程中包含了许多子程序,这些子程序实现了程序员在编程的时候经常需要用到的功能。程序员在编程的时候,可以用int指令直接调用BIOS和DOS提供的中断例程,来完成某些工作。
和硬件设备相关的DOS中断例程中,一般都调用了BIOS 的中断例程。
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中断例程应用
int 10h中断例程是BIOS提供的中断例程, 其中包含了多个和屏幕输出相关的子程序。
一般来说, 一个供程序员调用的中断例程中往往包括多个子程序,中断例程内部用传递进来的参数来决定执行哪一个子程序。BIOS 和DOS 提供的中断例程,都用ah来传递内部子程序的编号。
(ah)=2 表示调用第10h 号中断例程的2号子程序,功能为设置光标位置,可以提供光标所在的行号(80*25 字符模式下: 0~24) 、列号(80*25 字符模式下: 0~79) ,和页号作为参数。
(bh)=0, (dh)=5, (dl)= 12, 设置光标到第0 页, 第5 行, 第12 列。
bh中页号的含义:内存地址空间中, B8000H~BFFFFH共32kB 的空间,为80*25 彩色字符模式的显示缓冲区。一屏的内容在显示缓冲区中共占4000 个字节。
显示缓冲区分为8 页,每页4KB(4000B) ,显示器可以显示任意一页的内容。一般情况下,显示第0页的内容。也就是说,通常情况下, B8000H~B8F9FH中的4000 个字节的内容将出现在显示器上。
编程:在屏幕的5 行12 列显示3个红底高亮闪烁绿色的'a' 。
assume cs:code code segment mov ah,2 #置光标 mov bh,0 #第0 页 mov dh,5 #dh中放行号 mov dl,12 #dl 中放列号 int 10h mov ah,9 #在光标位置显示字符 mov al,'a' #字符 mov bl, 11001010b #颜色属性 mov bh,0 #第0页 mov cx,3 #字符重复个数 int 10h mov ax,4c00h int 21h code ends end
DOS中断例程应用
int 21h 中断例程是DOS 提供的中断例程,其中包含了DOS 提供给程序员在编程时调用的子程序。
int 21h中断例程的4ch号功能,即程序返回功能,如下:
mov ah,4ch #程序返回 mov al,0 #返回值 int 21h
(ah)=4ch表示调用第21h号中断例程的4ch号子程序,功能为程序返回,可以提供返回值作为参数。
int 21h中断例程在光标位置显示字符串的功能:
ds:dx指向字符串 #要显示的字符串需用“$“作为结束符 mov ah,9 #功能号9,表示在光标位置显示字符串 int 21h
(ah)=9 表示调用第21h 号中断例程的9 号子程序,功能为在光标位置显示字符串,可以提供要显示字符串的地址作为参数。
编程:在屏幕的5 行12 列显示字符串“Welcome to masm! " 。
assume cs:code data segment db 'Welcome to masm','$' data ends code segment start:mov ah,2 #置光标 mov bh,0 #第0 页 mov dh,5 #dh中放行号 mov dl,12 #dl 中放列号 int 10h mov ax,data mov ds,ax mov dx,0 #ds:dx指向字符串的首地址data:O mov ah,9 int 21h mov ax,4c00h int 21h code ends end start
上述程序在屏幕的5行12列显示字符串“Welcome to masm!", 直到遇见“$“(“$“本身并不显示,只起到边界的作用)。