概述
实验内容
编写0号中断处理程序,使得在除法溢出发生时,在屏幕中间显示字符串"divide error!",然后返回到DOS。
解题
这一章都在介绍中断,包括中断的产生、中断处理程序、中断向量表、中断过程、相关指令。
解决本次实验的前提是将本章的内容理解好,那么在完成这部分(原书第12章-内中断)之后,开始实验吧~
分析整个中断过程
(1)当发生除法溢出的时候,产生0号中断信息,从而引发中断。
CPU会完成以下工作:
- 取得中断类型码 0
- 标志寄存器入栈,TF、IF 设置为 0
- CS、IP入栈
- (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的地址找中断处理程序的段地址。
如下图:
总结一下将要做的事情:
- 编写可以显示“divide error!”的中断处理程序 do0;
- 将do0拷贝到内存0000:0200处;
- 将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 ;0:4H = 200H
mov word ptr es:[0 * 4 + 2], 0 ;0:0 = 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
效果展示
最后
以上就是大意眼神为你收集整理的汇编语言:实验十二 编写0号中断的处理程序的全部内容,希望文章能够帮你解决汇编语言:实验十二 编写0号中断的处理程序所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复