任何一个CPU都可以在执行完当前正在执行的指令之后,检测到从CPU外部发送过来的或内部产生的一种特殊信息,并且可以立即对所接受的信息进行处理。这种特殊的信息就是中断信息。中断的意思是指,CPU不再接着(刚执行完的指令)向下执行,而是转去处理这个特殊信息。中断信息可以来自内部或者外部,本文主要讨论来自CPU内部的中断信息,运行环境是16位8086CPU。
CPU在收到中断信息后,应该转去执行该中断信息的处理程序。我们知道,8086CPU如果要执行某处的程序,就要将CS:IP指向它的入口地址(即程序第一条指令的地址)。我们要解决的问题就是如何根据中断信息找到相关程序的入口地址。
其实在中断信息中含有相关中断源的类型码。中断类型码的作用就是用来定位中断处理程序。那么8086CPU是如何根据8位的中断类型码得到中断处理程序的段地址和偏移地址的呢?答案是中断向量表,中断向量表是中断处理程序入口地址的列表。
对于8086PC机,从内存 0000:0000
到 0000: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做除法的时候应该注意以下几点:
格式如下:
div reg div 内存单元
接下来我们回到程序的编写,我们大致的思路是这样的:
do0
; 0000:0200
处; 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位
进行设置:
cld
指令:将标志寄存器的df位设置为0 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
这里我们需要注意一点,下面这样单独定义数据段是不可行的:
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~BFFFFH
共 32KB
的空间,为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