9. int指令

196 阅读6分钟

int指令

中断信息可以来自CPU的外部和内部,当CPU的内部有需要处理的事情发生的时候,将产生需要马上处理的中断信息,引发中断例程。这一章中,我们学习另一种重要的内中断,由int指令引发的中断。

1. int指令

int指令的格式为:int n,n为中断类型码,它的功能是引发中断。CPU执行int n指令,相当于引发了一个n号中断的中断过程,执行过程如下:

  1. 取中断类型码;
  2. 标志寄存器入栈,IF=0,TF=0;
  3. CS、IP入栈;
  4. (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部分工作。

  1. 编写实现求平方功能的程序;
  2. 安装程序,将其安装在0:0200处;
  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, 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中主要包含以下几部分内容:

  1. 硬件系统的检测和初始化程序
  2. 外部中断和内部中断的中断例程;
  3. 用于对硬件设备进行I/O操作的中断例程;
  4. 其它和硬件系统相关的中断例程。

BIOS和DOS在所提供的中断例程中包含了许多子程序,这些子程序实现了程序员在编程的时候经常需要用到的功能。程序员在编程的时候,可以用int指令直接调用BIOS和DOS提供的中断例程,来完成某些工作。

5. BIOS和DOS中断例程的安装过程

  1. 开机后,CPU加电,初始化(CS) = 0FFFFH, (IP) = 0, 自动从FFFF:0单元开始执行程序。FFFF:0处有一条跳转指令,CPU执行该指令后,转去执行BIOS中的硬件检测和初始化程序。
  2. 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。注意,对于BIOS所提供的中断例程,只需将入口地址登记在中断向量表中即可,因为它们是固化到ROM中的程序,一直在内存中存在。
  3. 硬件系统检测和初始化完成后,调用int 19H进行操作系统的引导。从此将计算机交由操作系统控制。
  4. 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