汇编12:内中断

内中断

CPU可以在执行完当前正在执行的指令之后,检测到从CPU外部或内部产生的一种特殊信息,并且可以立即对所接受到的信息进行处理。这种特殊信息一般称其为中断信息。中断就是CPU不再接着刚执行完的指令向下执行,而是转而去处理这个特殊信息。

内中断的产生

从CPU内部产生的中断信息被称为内中断。当下列情况发生时会产生内中断:

1、除法错误,如执行div指令时产生的除法溢出

2、单步执行

3、执行into指令

4、执行int指令

CPU根据中断类型码来区分不同的中断信息,中断类型码是一个字节型数据,可以表示256种中断信息的来源,产生中断信息的来源简称为中断源。上述四种情况下的中断类型码如下:

1、除法错误:0

2、单步执行:1

3、执行into指令:4

4、执行int指令:中断类型码为指令后的字节型立即数

扫描二维码关注公众号,回复: 11159331 查看本文章

中断处理程序和中断向量表

CPU收到中断信息后,需要对中断信息进行处理,用来处理中断信息的程序被称为中断处理程序。

转去执行中断处理程序需要让CS:IP指向该程序的入口,这个过程需要用到中断向量表。CPU用8位的中断类型码通过中断向量表找到相应的中断处理程序的入口地址,中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口:

对于8086CPU,中断向量表指定放在内存地址0处,从0000:0000到0000:03FF的1024个单元中存放着中断向量表。一个表项中放着一个中断处理程序的入口地址,包含段地址和偏移地址,占用两个字,高地址字存放段地址,低地址字存放偏移地址。

中断过程

CPU在执行完中断处理程序后,应该返回原来的执行点继续执行下面的命令。所以在中断过程中应该将原来的CS和IP保存起来。

中断过程如下:

1、根据中断信息取得中断类型码

2、标志寄存器的值入栈(中断处理程序可能会修改标志寄存器,所以这里要保存起来准备恢复)

3、设置标志寄存器的第8位TF和第9位IF的值为0

4、CS的内容入栈

5、IP的内容入栈

6、从内存地址为中断类型码*4的位置取出两个字,组成中断处理程序的入口,设置IP和CS

中断处理程序和iret指令

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

1、保存用到的寄存器

2、处理中断

3、恢复用到的寄存器

4、用iret指令返回

iret指令相当于执行:

pop IP
pop CS
popf

相当于把执行中断处理程序之前保存的内容(CS:IP和标志寄存器)全部恢复了。

编程处理0号中断

如果我们debug以下程序:

mov ax,1000h
mov bh,1
div bh

就会发生除法溢出错误,可以看到当产生中断时,控制台显示信息“Divide overflow”,这就是系统对0号中断的处理。

我们考虑改变0号中断处理程序的功能,要让其在屏幕中间显示“overflow”,然后返回操作系统。

这个程序的步骤如下:

1、产生0号中断信息,引发中断过程,CPU将进行如下工作:

取得中断类型码0、标志寄存器入栈然后TF、IF设置为0、CS和IP入栈、CS:IP根据中断向量表的完成跳转,进而执行中断处理程序。

2、中断处理程序的步骤如下:

相关处理、向显示缓冲区发送字符串“overflow”,然后返回DOS。我们称这段程序为do0。这个do0程序应该放在0000:0200处,这段空间是中断向量表的空闲单元,不会有程序占用。确定了do0程序的位置,就应该调整中断向量表的表项,应该要将do0的段地址0存放在0000:0002字单元中,将偏移地址200H存放在0000:0000字单元中。

综上所述,程序的框架如下:

assume cs:code
code segment
start:	do0安装程序
	设置中断向量表
	mov ax,4c00h
	int 21h
		
do0:	显示字符串“overflow”
	mov ax,4c00h
	int 21h
code ends
end start

这个程序执行时do0程序是不执行的,只是为了能让中断程序找到do0程序。

安装程序和设置中断向量

我们可以用movsb指令来将do0的代码移动到0:200处:

start:	mov ax,cs
	mov ds,ax
	mov si,offset do0						设置ds:si指向源地址
	mov ax,0
	mov es,ax
	mov di,200h							设置es:di指向目的地址
		
	mov cx,offset do0end-offset do0			                设置传输长度
	cld								设置传输方向
	rep movsb
		
	设置中断向量表
	mov ax,0
	mov es,ax
	mov word ptr es:[0*4],200h
	mov word ptr es:[0*4+2],0
	mov ax,4c00h
	int 21h

这里注意因为do0代码段的长度是可变的,所以这里用offset do0end-offset do0来代表代码的长度,减号是编译器识别的符号。

do0程序

程序如下:

do0:	jmp short do0start
	db "overflow!"
do0start:	mov ax,cs
		mov ds,ax
		mov si,202h							设置ds:si指向字符串
			
		mov ax,0b800h
		mov es,ax
		mov di,12*160+36*2					        设置es:di指向显存空间的中间位置
			
		mov cx,9							设置cx为字符串长度
	s:	mov al,[si]
		mov es:[di],al
		inc si
		add di,2
		loop s
			
		mov ax,4c00h
		int 21h
do0end:nop

这里要打印的字符串overflow也必须放在do0程序中,因为设置好中断程序后,这个程序随时都有可能执行,也就是说除了中断程序之外的数据都有可能被其他数据覆盖。

单步中断

我们在debug时如果使用t命令,CPU就能执行一条指令后,就显示各个寄存器的状态,这是因为CPU有一种机制被称为单步中断,当CPU执行完一条指令时,如果检测到标志寄存器中的TF位为1,则产生单步中断,引发中断过程,此时它的中断类型码为1.

debug就是因为将TF设置为1,使得CPU工作于单步中断方式下。在进入中断处理程序之前,TF值要设置为0,这是为了不让中断处理程序也引起中断导致无限中断。

不响应中断的特殊情况

有些情况下即使发生中断,CPU也不会响应。

其中之一就是执行完向ss寄存器传送数据的指令后,不会响应中断。这是因为ss和sp通常是连续设置完成的,如果ss设置完了之后sp还没设置就产生中断,这使中断时ss:sp指向错误的栈顶,让中断过程压入栈中的数据可能覆盖有用的数据。所以CPU在执行完设置ss的指令后,不响应中断,所以在编程的时候sp的设置应该紧跟ss之后,不宜离太远。

猜你喜欢

转载自www.cnblogs.com/yinyunmoyi/p/12811552.html