转载

汇编语言之内中断处理

任何一个CPU都可以在执行完当前正在执行的指令之后,检测到从CPU外部发送过来的或内部产生的一种特殊信息,并且可以立即对所接受的信息进行处理。这种特殊的信息就是中断信息。中断的意思是指,CPU不再接着(刚执行完的指令)向下执行,而是转去处理这个特殊信息。中断信息可以来自内部或者外部,本文主要讨论来自CPU内部的中断信息,运行环境是16位8086CPU。

中断向量表

CPU在收到中断信息后,应该转去执行该中断信息的处理程序。我们知道,8086CPU如果要执行某处的程序,就要将CS:IP指向它的入口地址(即程序第一条指令的地址)。我们要解决的问题就是如何根据中断信息找到相关程序的入口地址。

其实在中断信息中含有相关中断源的类型码。中断类型码的作用就是用来定位中断处理程序。那么8086CPU是如何根据8位的中断类型码得到中断处理程序的段地址和偏移地址的呢?答案是中断向量表,中断向量表是中断处理程序入口地址的列表。

对于8086PC机,从内存 0000:00000000:03FF 的1024个单元中存放着中断向量表。里面存放着256个中断源所对应的入口地址。每一个表项存放一个中断向量,也就是一个中断处理程序的入口地址,一个表项占两个字, 高地址存放段地址,低地址存放偏移地址

中断过程

通过上面的分析,我们知道,可以用中断类型码,在中断向量表中找到中断处理程序的入口,然后用它来设置CS和IP,这个过程就叫做中断处理过程:

(1)取得中断类型码
(2)pushf
(3)TF=0,IF=0
(4)push CS
(5)push IP
(6)(IP)=(N*4), (CS)=(N*4+2)

在最后一步完成以后,CPU开始执行由程序员编写的中断处理程序。

中断处理程序的编写方法和子程序的比较相似,下面是常规步骤:

(1)保存用到的寄存器
(2)处理中断
(3)恢复用到的寄存器
(4)用iret指令返回

iret 指令的功能等价于:

pop IP
pop CS
popf

实战

当CPU执行div等除法指令的时候,如果发生了除法溢出错误,将产生中断类型码为0的信息,当CPU检测到这个信息,然后引发中断过程,转去执行0号中断所对应的中断处理程序。

编程:当发生除法溢出时,在屏幕中间显示”overflow!”,返回DOS。

div 是除法指令,使用div做除法的时候应该注意以下几点:

  1. 除数:有8位和16位两种,在一个reg或内存单元中。
  2. 被除数:如果除数为8位,被除数则为16位,默认在AX中存放;如果除数为16位,被除数则为32位,在DX和AX中存放,DX中存放高16位,AX中存放低16位。
  3. 结果:如果除数为8位,则AL中存放除法操作的商,AH中存放除法操作的余数;如果除数为16位,则AX存储除法操作的商,DX中存储除法操作的余数。

格式如下:

div reg
div 内存单元

接下来我们回到程序的编写,我们大致的思路是这样的:

  1. 编写可以显示“overflow!”的中断处理程序: do0
  2. 将do0送入内存 0000:0200 处;
  3. 将do0的入口地址从0000:2000存放在中断向量表0号表项中。

ps: 一般情况下,从0000:0200至0000:2F00的256个字节的空间所对应的中断向量表项都是空的,操作系统和其他应用程序一般都不占用。

程序的框架如下:

assume cs:code

code segment

 start: do0安装程序
 设置中断向量表
 mov ax, 4c00h
 int 21h

 do0: 显示字符串"overflow!"
 mov ax, 4c00h
 int 21h

code ends
end start

安装

先解释一下 movsb 指令,用汇编语言描述movsb指令的功能如下:

mov es:[di], byte ptr ds:[si] ;8086并不支持这样的指令,这里只是为了方便描述

如果df=0:
inc si
inc si

如果df=1:
dec si
dec di

mobsw和movsb类似,只是将si和di递增或递减2.一般来说,movsb和mobsw都支和 rep 配合使用,格式如下:

rep movsb
`

用汇编语言来描述就是rep movsb的功能就是:

s:movsb
 loop s

可见,rep的作用就是根据cs的值,重复执行后面的串传送指令。由于没执行一次movsb指令si和di都会递增后递减指向后一个单元或前一个单元,则rep movsb指令就可以循环实现(cx)各字符的传送。

8086CPU提供了下面两条指令对 df位 进行设置:

  1. cld 指令:将标志寄存器的df位设置为0
  2. std 指令:将标志寄存器的df位设置为1

可以使用movsb指令,将do0的代码送入0:200处,程序如下:

 start: mov ax, cs
 mov ds, ax
 mov si, offset do0 ;设置ds:si指向ds:si指向源地址

 mov ax, 0
 mov ex, ax
 mov di, 200h ;设置es:di指向目的地址

 mov cx, offset do0end-offset do0 ;设置cx为传输长度

 cld ;设置传输方向为正
 rep movsb

 设置中断向量表

 mov ax, 4c00h
 int 21h

 do0: 显示字符串"overflow!"
 mov ax, 4c00h
 int 21h

do0end: nop

do0的编写

这里我们需要注意一点,下面这样单独定义数据段是不可行的:

data segment
 db "overflow!"
dat ends

由于一旦发生除法溢出,do0就会被执行,就是说do0会被执行多次。而我们上面的程序执行完成之后返回,它所占用的内存会被系统释放,而在其中存放”overflow”可能也将被别的信息覆盖。而do0程序已经被安装放入了0:200处。

 do0: jmp short do0start
 db "overflow"

do0start: mov ax, cs
 mov ds, ax
 mov si, 202h ;设置ds:si指向字符串注意jmp short也要占用2个字节

 mov ax, 0b00h
 mov es, ax
 mov di, 12*160+36*2 ;设置es:di指向显存空间的中间位置
 
 mov cx, 9

 s: mov al, [si]
 mov es:[di], al
 inc si
 add di, 2
 loop s

 mov ax, 4c00h
 int 21h

补充:内存空间中, B8000H~BFFFFH32KB 的空间,为80*25彩色字符模式的显示缓冲区。80*25模式下,一屏的内容中共占4000个字节。显示缓冲区分为8页,每页 4KB (大约4000B),一般情况下,显示第0页内容。也就是说通常情况下,B8000H~B8F9FH的4000个字节的内容将出现在显示器上。其中一个字符占用两个字节的存储空间, 低位字节存储字符的ASCII码,高位字节存储字符的属性 ,比如黑底绿字的A就是4102H,其中41H表示A的ASCII码的值,02H代表黑底绿字。

设置中断向量

下面将do0的入口地址0:200,写入中断向量表的0号表项中,使do0称为0号中断程序的中断处理程序。

0号表项的地址为0:0,其中0:0字单元中存放偏移地址,0:2字单元中存放段地址。程序如下:

mov ax, 0
mov es, ax
mov word ptr es:[0*4], 200h
mov word ptr es:[0*4+2], 0
原文  http://www.ziwenxie.site/2017/02/17/assembly-interrupt/
正文到此结束
Loading...