汇编语言案例:编译处理0号中断

——王爽 《汇编语言(第3版)》实验12 清华大学出版社

案例:
改变0号中断程序的功能,重新编写一个0号中断程序,它的功能是在屏幕中间显示“overflow!”。

一、什么叫中断,什么叫0号中断?

任何一个通用CPU,可以在执行完当前正在执行的指令之后,检测到CPU外部发送过来的(外中断)或者内部产生的(内中断)一种特殊信息,并且可以对所接收的信息进行处理,这种特殊的信息,叫做中断信息

什么情况的发生会产生中断信息?
(1)除法溢出,比如,执行div指令产生的除法溢出。
(2)单步执行。
(3)执行into指令。
(4)执行int指令。

下面引入中断类型码的概念:
CPU要对这4种情况可以产生需要及时处理的中断信息进行不同的处理,就要知道这四种中断信息的来源。中断类型码用来标识中断信息的来源。
上面4种中断源对应的中断类型码:
(1)除法溢出 0
(2)单步执行 1
(3)执行info指令 4
(4)int指令 int指令后面跟的n就是中断类型码

0号中断,就是中断类型码为0的中断,就是除法溢出中断。

二、关于除法溢出的解释

div指令:除法指令。
在这里插入图片描述
什么叫除法溢出?分为两种情况:
(1)除数为0,会发生溢出。
(2)商的精度损失:
     (2.1)如果除数是字(16位),则被除数(32位)存在dx和ax里,(除后商存在ax里,余数存在dx里)如果除后商大于一个字,就会产生精度损失溢出。范围65535。
    (2.2)如果除数是字节(8位),则被除数(16位)存在ax里,(除后商存在al里,余数存在ah里)如果除后商大于一个字节,就会产生精度损失溢出。范围255。

三、中断过程及中断向量表

8086CPU在收到中断信息后,引发的中断过程:
(1)(从中断信息中)获得中断类型码。
(2)标志寄存器的值入栈。因为在中断过程中要改变标志寄存器的值,先保存。
(3)设置标志寄存器的第8位TF和第9位IF的值为0。
(4)CS的内容入栈。
(5)IP的值入栈
(6)从内存地址为中断类型码*4中断类型码*4+2的两个字单元中读取中断处理程序的入口地址设置IP和CS。

简洁描述中断过程:
(1)取得中断类型码N
(2)pushf
(3)TF=0,IF=0
(4)push CS
(5)push IP
(6)(IP)=(N4),(CS)=(N4+2)

中断向量表:用来处理程序入口地址的列表。
中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口。8086支持256个中断,但系统要处理的中断事件远没有256个。故在中断向量表中,许多单元是空的。内存单元:0000:0000 ~ 0000:03FF1024个单元。每个表项占两个字,高地址字存放段地址,低地址字存放偏移地址

iret指令,中断返回指令,描述:
(1)pop ip
(2)pop cs
(3)popf

四、案例分析

案例:
改变0号中断程序的功能,重新编写一个0号中断程序,它的功能是在屏幕中间显示“overflow!”。

分析:
0号中断程序,即除法溢出。编程实现:当发生除法溢出,屏幕中央输出“overflow!”,返回DOS。
(1)当发生除法溢出时,产生0号中断信息,从而引发中断过程。
    a.取得中断地址码0
    b.标志寄存器入栈,TF、IF设置为0
    c.CS、IP入栈
    d.(IP)=(0 * 4),(CS)=(0 * 4+2)
(2)当中断0发生时,CPU将转去执行中断处理程序
    a.相关处理
    b.向显示缓冲区送字符串“overflow!”
    c.返回DOS
(3)现在的问题是,do0应存放在内存中。因为除法溢出随时都可能发生,CPU随时都可能将CS:IP指向do0的入口,执行程序。只需要找到一块别的不会用到的内存区,把do0传送到其中,而中断向量表(0000:0000~0000:03FF)有许多空的单元。【可以将do0传送到内存0000:0200处
为什么是这个空间?因为这个空间是中断向量表为空的部分。一般来说,中断向量表的0000:0200~0000:02FF都是空的。
(4)将中断处理程序do0放到0000:0200后,若要使除法溢出时,CPU转去执行do0,就必须将do0的入口地址(0000:0200) 登记在中断向量表对应的表项中。
中断向量表的对应表项:
段地址:0 * 4+2
偏移地址:0 *4
也就是要将do0的段地址0存放在0000:0002字单元中,偏移地址200H存放在0000:0000字单元中

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

第一步:

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

说明:
1.程序执行时被加载到内存中,此时do0的代码在程序所在的内存空间中,它只是放在程序的代码段中的一段要被传送到其他单元的数据,此时我们不能说它是0号中断的中断处理程序
2.程序中安装do0的代码执行完后,do0的代码被从程序的代码段中复制到0:200处,此时它只不过是放在0:200处的一些数据,我们也不能说它是0号中断的中断处理程序
3.程序中设置中断向量表的代码执行完毕后,在0号表项中填入了do0的入口地址0:200,此时0:200处的信息,即do0的代码,就变成了0号中断的中断处理程序。此时,当0号中断发出时,CPU将执行0:200处的代码。

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

第二步:do0安装程序。(将do0处的代码复制到0:200处,使用movsb)
使用rep movsb:
1.目的地址es:di 0:200
2.源地址ds:si cs:offset do0
3.传输长度cx do0(传输长度就是代码部分长度)
4.传输方向:cld正向,std负向。 (cld)

assume cs:code
code segment
	start:  设置es:di指向目的地址
		设置ds:si指向源地址
		设置cx为传输长度
		设置传输方向为正
		rep movsb		

		设置中断向量表

		mov ax,4c00h
		int 21h

	d0:	显示字符串“overflow!”
		mov ax,4c00h
		int 21h
code ends
end start	

代码实现:

assume cs:code
code segment
	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,do0部分代码长度	;设置cx为传输长度
		cld			;设置传输方向为正
		rep movsb

		设置中断向量表

		mov ax,4c00h
		int 21h

	do0:	显示字符串“overflow!”
		mov ax,4c00h
		int 21h
code ends		
end start

问题:怎么计算do0的长度?
可以利用编译器来计算do0的长度,
offset do0end-offset do0
do0end nop
(-是用来表示两个常数的减法的)

assume cs:code
code segment
	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		;设置cx为传输长度
		cld			;正向传输方向
		rep movsb

		设置中断向量表
		mov ax,4c00h
		int 21h

	do0:	显示字符串“overflow!”
		mov ax,4c00h
		int 21h
do0end:	nop
code segment		
end start

第三步:do0程序,主要任务是显示字符串
注意:“overflow!”不能再data段用db定义,因为如果在data段定义,程序执行完后会直接返回,它所占的内存空间将被系统释放,其中存放的“overflow!”也将被其他信息覆盖。即使do0被放在了0:200处,随时会因为除法溢出而被CPU执行,但本该取出的“overflow!”很可能会被其他信息覆盖。

assume cs:code
code segment
	start:	mov ax,cs
		mov ds,ax
		mov si,offset do0	;ds:si指向源地址

		mov ax,0
		mov es,0
		mov di,200h		;es:di指向目标地址

		mov cx offset do0end-offset do0		;设置cx为传输长度
		cld
		rep movsb

		设置中断向量表
		mov ax,4c00h
		int 21h

	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
code ends
end start

问题1:jmp short do0start 的作用:
因为在程序开始的“overflow!”不是可以指向的代码,所以在“overflow!”之前加上一条jmp的指令,转移到正式的do0程序。当除法发生溢出时,CPU执行0:200处的jmp指令,跳过后面的字符串,转到正式的do0处执行。

问题2:“overflow!”存在哪?
段地址:“overflow!”和do0的代码处于同一段中,当除法发生溢出时,CS中必然存放着do0和段地址,也就是“overflow!”的段地址。
偏移地址0:200处的指令为jmp short do0start,这条指令占两个字节,所以“overflow!”的偏移地址为202h

第四步:设置中断向量表
作用:将do0的入口地址0:200,写入中断向量表的0号表项中,使do0成为0号中断的中断处理程序。(0号表项的地址为0:0,其中0:0单元存放偏移地址,0:2单元存放段地址)

mov	word ptr es:[0*4],200h
mov	word ptr es:[0*4+2],0

五、代码

assume cs:code
code segment
	start:	mov ax,cs
		mov ds,ax
		mov si,offset do0
		mov ax,0
		mov es,ax
		mov di,200h
		mov cx,offset do0end-offset do0
		cld
		rep movsb
		mov word ptr es:[0*4],200h
		mov word ptr es:[0*4+2],0

	do0:	jmp short do0start
		db  'overflow!'
	
   do0start:	mov ax,cs
   		mov ds,ax
   		mov si,202h

		mov ax,0b800h
		mov es,ax
		mov di,12*160+36*2
		
		mov cx,9
	s:	mov al,[si]
		mov es:[di],al
		inc si
		add di,2
		loop s

		mov ax,4c00h
		int 21h
do0end:nop
code ends
end start
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值