1.       int指令

 

n       int格式: int nn为中断类型码。它的功能是引发中断过程。

n       CPU 执行int n指令,相当于引发一个 n号中断的中断过程,执行过程如下:

n       1)取中断类型码n

n       2)标志寄存器入栈,IF = 0TF = 0

n       3CSIP入栈;

n       4(IP) = (n*4)(CS) = (n*4+2)

n       从此处转去执行n号中断的中断处理程序

 

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

n       这个程序在 Windows 2000中的 DOS方式下执行时,将在屏幕中间显示一个,然后显示“Divide overflow”后返回到系统中。

n       是我们编程显示的,而,“Divide overflow”是哪里来的呢?

   我们的程序中又没有做除法,不可能产生除法溢出。

n       程序是没有做除法,但是在结尾使用了int 0指令。

n       CPU执行int 0指令时,将引发中断过程,执行 0号中断处理程序,而系统设置的 0号中断处理程序的功能是显示“Divide overflow”,然后返回到系统。

n       可见,int 指令的最终功能和call指令相似,都是调用一段程序。

n       一般情况下,系统将一些具有一定功能的子程序,以中断处理程序的方式提供给应用程序调用。

2.编写供应用程序调用的中断例程

 

n       示例一 编写、安装中断7ch的中断例程:

n       功能:求一word型数据的平方。

n       参数: (ax)=要计算的数据。

n       返回值:dxax中存放结果的高16位和低16位。

n       应用举例:求2*3456^2

assume cs:code

 code segment

 start: mov ax,3456;(ax)=3456

         int 7ch;调用中断7ch的中断例程,计算ax中的数据的平方

         add ax,ax;dx:ax存放结果,讲结果乘以2

         mov ax,4c00h

         int 21h

 code ends

 end start

n       我们要做三部分工作:

n       1)编程实现求平方功能的程序;

n       2)安装程序,我们将其安装在0:200处;

n       3)设置中断向量表,将程序的入口地址保存在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,200h

 

      mov cx,offset sqlend-offset sqr

      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

sqlend:nop

 

code ends

end start

 

n       示例二 编写、安装中断7ch的中断例程:

n       功能:将一个全是字母,以 0结尾的字符串,转化为大写。

n       参数:ds:si指向字符串的首地址。

n       应用举例:

  data段中的字符转化为大写。

n       应用举例:将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

 

3. intiret和栈的深入理解

n       问题:用7ch中断例程完成 loop指令的功能。

n       loop s 的执行需要两个信息,循环次数和到s的位移,所以,7ch中断例程要完成loop指令的功能,也需要这两个信息作为参数。

n       我们用cx存放循环次数,用bx存放位移。

n       应用举例:在屏幕中间显示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

n       分析:为了模拟loop指令,7ch中断例程应具备下面的功能:

n       1dec cx

n       2)如果(cx)0,转到标号s 处执行,否则向下执行。

n       下面我们分析7ch中断例程如何实现到目的地址的转移:

n       1)转到标号s显然应设(CS)=标号s的段地址,(IP)=标号s的偏移地址;

n       2)那么,中断例程如何得到标号s的段地址和偏移地址呢? 分析

n       3)现在知道,可以从栈中直接和间接地得到标号s的段地址和偏移地址,那么如何用它们设置CS:IP呢? 分析

n       int 7ch引发中断过程后,进入 7ch中断例程,在中断过程中,当前的标志寄存器、CSIP都要压栈,此时压入的CSIP中的内容,分别是调用程序的段地址(可以认为是标号 s 的段地址)和int 7ch后一条指令的偏移地址(即标号se的偏移地址)。

 

 

n       可见,在中断例程中,可以从栈里取得标号s 的段地址和标号 se的偏移地址,而用标号se的偏移地址加上bx中存放的转移位移就可以得到标号s的偏移地址。

n       可以利用iret指令,我们将栈中的se的偏移地址加上 bx 中的转移位移,则栈中的se的偏移地址就变为了s的偏移地址。

n       我们再使用iret指令,用栈中的内容设置CSIP,从而实现转移到标号s处。

n       7ch中断例程如下:

   lp:     push bp

            mov bp,sp

                dec cx

                jcxz lpret

                add [bp+2],bx

   lpret: pop bp

                iret

n       因为要访问栈,使用了 bp,在程序开始处将bp 入栈保存,结束时出栈恢复。

n       当要修改栈中se的偏移地址的时候,栈中的情况为;

n       栈顶处是bp 原来的数值,下面是se的偏移地址,再下面是s的段地址,再下面是标志寄存器的值。

n       而此时,bp中为栈顶的偏移地址,所以((ss)*16+(bp)+2)处为se 的偏移地址,将它加上bx 中的转移位移就变为s的偏移地址。最后用iret出栈返回,CS:IP即从标号s处开始执行指令。

n       如果(cx)=0,则不需要修改栈中 se的偏移地址,直接返回即可。

n       CPU从标号se处向下执行指令。

 

 

 

 

 

 

 

posted on 2009-04-05 23:07  jasonM  阅读(621)  评论(0编辑  收藏  举报