汇编语言:实验十二 编写0号中断的处理程序

实验内容

编写0号中断处理程序,使得在除法溢出发生时,在屏幕中间显示字符串"divide error!",然后返回到DOS。

解题

这一章都在介绍中断,包括中断的产生、中断处理程序、中断向量表、中断过程、相关指令。
解决本次实验的前提是将本章的内容理解好,那么在完成这部分(原书第12章-内中断)之后,开始实验吧~

分析整个中断过程

(1)当发生除法溢出的时候,产生0号中断信息,从而引发中断。
CPU会完成以下工作:

  1. 取得中断类型码 0
  2. 标志寄存器入栈,TF、IF 设置为 0
  3. CS、IP入栈
  4. (IP) = (0 * 4), (CS) = (0 * 4 + 2)

对于第二步,为社么要将TF、IF设置为0?
在十一小节中王爽老师给出了解答:CPU在执行完一条指令之后,如果检测到标志寄存器TF位为1,则产生单步中断,引发中断过程。单步中断的中断类型码为1,它引发的中断过程如下:
在这里插入图片描述
这也是本问题的关键了,反过来想,如果TF不设置为0,很显然CPU在即将执行中断处理程序的第一条指令时检测到了TF为1,就会在执行完该条指令之后继续进入到单步中断的过程当中… 就这样可能无限循环下去了,所以在中断处理之前必须将TF置为0。
对于IF书中没有多余解释,对此笔者搜集到了以下资料:
在这里插入图片描述
↑Linux系统中的中断分类

只看表格中的第二行第一条的处理方式一栏:清除标志寄存器eflags的IF标志可屏蔽中断,笔者推测IF标志位的功能就是显示中断的开(On)关(Off),可以译为:Interrupt Flag,
(查阅了其他博客也确实是这样)

IF:中断允许标志位。它用来控制8086是否允许接收外部中断请求。若IF=1,8086能响应外部中断,反之则屏蔽外部中断;

这么想就能够串通了,只是用来屏蔽掉其他中断(处理中断的过程中关闭中断功能,确保能够顺利执行完本次中断,处理完成后再打开中断)

好了,现在明白CPU在发生中断之后做的事情了,那么我们需要完成什么呢?
1.相关处理
2.向显示缓冲区写入想要显示的字符串 “divide error!”
3.返回 DOS

按照王爽老师的讲解,将这段程序命名为 do0。

但是一个从未碰到过的问题来了:do0程序应该放到哪?
do0应该放到内存中,因为除法溢出随时可能发生,CPU随时都可能将CS:IP指向do0的入口,然后执行它。

按理来说我们需要向操作系统申请一块空间去放置do0程序,但是过多的讨论申请内存将偏离问题的主线,所以这里简单做:直接使用一块别的程序不会用到的内存区,将do0拷贝到其中就可以了。

12.3 中断向量表 章节中作者讲解了中断向量表的存储位置(针对8086CPU):
0000:0000 到 0000:03FF 共计 400H (1024字节)个单元中存放了256个中断,但是实际上系统中要处理的中断事件没有达到256个,所以在表中有许多单元是空着的。

根据书中的指示:0000:0200 到 0000:02FF 的256个字节的空间所对应的单元都是空的
因此,可以将do0拷贝到内存0000:0200处

do0程序的放置解决了,接下来就是当发生除法溢出的时候,CPU会取得中断码0,然后到(4 * 0) = 0H的地址(0000:0000H)去找中断处理程序的偏移地址,到(4 * 0 + 2) = 2H的地址找中断处理程序的段地址。
如下图:
在这里插入图片描述

总结一下将要做的事情:

  1. 编写可以显示“divide error!”的中断处理程序 do0;
  2. 将do0拷贝到内存0000:0200处;
  3. 将do0的入口地址0000:0200 存储在中断向量表0号单元中。

程序框架:
在这里插入图片描述

安装do0程序与设置中断向量表

需要明晰的是:我们编写的程序在运行时do0处的代码是不执行的!
我们要做的是将do0这部分代码放入到之前选择好的那块没有程序会使用的内存空间0000:0200H上,并且设置好中断向量表中0号单元的内容,这样CPU就可以在发生除法溢出的时候乖乖地去0号向量表单元取出对应的偏移地址、段地址,然后顺利完成中断处理程序do0的执行。

安装do0程序

将编写好的do0程序代码送入到0000:0200H内存段中,需要使用到 movsb指令,如下(由于csdn没有比较好展示汇编代码的方式,这里笔者先放上Notepad++中的截图方便查看):

在这里插入图片描述
上面就是do0程序的安装过程,其中需要注意的是第18行:
mov cx, offset do0end - offset do0
为了计算do0段的大小,需要额外设置标号 do0end 放在do0程序段结束的位置。
注:如果只是单纯的计算出所编写的do0程序块大小,然后赋值给cx,这种方式的编程很明显是不可取的,因为do0稍微改一下可能影响程序段的大小,又得重新计算,所以使用标号法交给编译器去做这些麻烦的事情吧~

设置中断向量表

对于0号中断,我们需要在0000:0000处填上偏移地址,在0000:0004处填上中断处理程序段地址。
在这里插入图片描述
注:对于N号中断,偏移地址应该填在0000:(N * 4) 字单元中,段地址应该填在0000:(N * 4 + 2) 字单元中。

编写do0程序

do0程序需要做的:
1.相关处理
2.向显示缓冲区写入想要显示的字符串 “divide error!”
3.返回 DOS

这个相关处理指的是什么?
先别急,看第二条:向显示缓冲区写入想要显示的字符串 “divide error!”
这个我们熟悉吧?不熟悉请点这里-> 在显示缓冲区内编程完成字符串显示
这个不难,但是既然是字符串的显示,字符串的存储位置也是需要考虑的问题。

在这里插入图片描述
想一想:像上图这样放在代码的起始位置行不行?
答案是不行,因为那样字符串的位置是不够“安全”的,因为在整个程序(完成拷贝的程序,不是do0程序)结束之后,原来的空间会被释放(别的程序可能会用到它),那么难免这块空间会被修改,我们需要一块在do0程序被执行时装有字符串的空间,说起来比较抽象,看代码:
在这里插入图片描述

完整代码

assume cs:code

;编写0号中断程序,使得在除法溢出发生时,
;在屏幕中显示字符串"divide error!"
;然后返回dos

code segment
start:
	;首先将中断处理程序送入到中断向量地址处
	mov ax, cs
	mov ds, ax
	mov si, offset do0	;源地址 cs:offset do0
	
	mov ax, 0
	mov es, ax
	mov di, 200H	;目标地址 0:200H
	
	mov cx, offset do0end - offset do0	;用标号计算出do0段的大小
	cld	;设置si、di递增
	rep movsb
	
	;设置中断向量表
	mov ax, 0
	mov es, ax
	mov word ptr es:[0 * 4], 200h	;04H = 200H
	mov word ptr es:[0 * 4 + 2], 0	;00 = 0H
	
	mov ax, 4c00h
	int 21h
	
	
do0:
	jmp short do0start
	db "divide error!"
	
do0start:;中断程序执行开始处:打印字符串
	mov ax, cs
	mov ds, ax
	mov si, 202H
	
	mov ax, 0b800H
	mov es, ax	;找到显卡位置
	mov di, 160 * 12 + 36 * 2	;显示在显卡中间
	
	mov cx, 13	;一共13个字符,挨个拷贝
	mov dh, 11000010B	;显示 红底闪烁绿字
	
s:
	mov dl, [si]	;将需要显示的字符串放到dl
	mov es:[di], dl	;输送到显卡处
	mov es:[di + 1], dh;设置字的属性
	inc si	;往后偏移一个字节
	add di, 2;往后偏移一个字
	loop s
	
	mov ax, 4c00h
	int 21H
do0end:
	nop
code ends
end start

效果展示

在这里插入图片描述

  • 10
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nepu_bin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值