📌 汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。 本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。
中断是CPU处理外部突发事件的一个重要技术。
任何一个通用的CPU,比如8086,都具备一种能力,可以在执行完当前正在执行的指令之后,检测到从CPU外部发送过来的或内部产生的一种特殊信息,并且可以立即对所接收到的信息进行处理。
这种特殊的信息,我们可以称其为:中断信息。
中断的意思是指,CPU 不再接着(刚执行完的指令)向下执行,而是转去处理这个特殊信息。
注意,我们这里所说的中断信息,是为了便于理解而采用的一种逻辑上的说法。它是对几个具有先后顺序的硬件操作所产生的事件的统一描述。
“中断信息”是要求CPU马上进行某种处理,并向所要进行的该种处理提供了必备的参数的通知信息。因为内容不是微机原理与接口或组成原理,我们只能用一些便于理解的说法来描述一些比较复杂的机器工作原理,从而使学习者忽略一些和我们的学习重心无关的内容。
引起中断的原因或者说发出中断请求的来源叫做中断源。
根据中断源的不同,可以把中断分为硬件中断和软件中断两大类,而硬件中断又可以分为外部中断和内部中断两类。
软件中断其实并不是真正的中断,它们只是可被调用执行的一般程序以及DOS的系统功能调用(INT 21H)等都是软件中断。
CPU为了处理并发的中断请求,规定了中断的优先权,中断优先权由高到低的顺序是:
(1)除法错、溢出中断、软件中断
(2)不可屏蔽中断
(3)可屏蔽中断
(4)单步中断。
当CPU的内部有什么事情发生的时候,将产生需要马上处理的中断信息呢?
对于8086CPU,当CPU内部有下面的情况发生的时候,将产生相应的中断信息。
(1)除法错误,比如,执行 div 指令产生的除法溢出;
(2)单步执行;
(3)执行 into 指令;
(4)执行 int 指令。
我们现在不要去管这4种情况的具体含义,只要知道CPU内部有4种情况可以产生需要及时处理的中断信息即可。
虽然我们现在并不很清楚,这4种情况到底是什么,但是有一点是很清楚的,即,它们是不同的信息。既然是不同的信息,就需要进行不同的处理。要进行不同的处理,CPU首先要知道,所接收到的中断信息的来源。
所以中断信息中必须包含识别来源的编码。8086CPU用称为中断类型码的数据来标识中断信息的来源。
中断类型码为一个字节型数据,可以表示256种中断信息的来源。
上述的4种中断源,在8086CPU中的中断类型码如下。
(1)除法错误:0
(2)单步执行:1
(3)执行 into 指令:4
(4)执行 int 指令,该指令的格式为int n,指令中的n为字节型立即数,是提供给CPU 的中断类型码。
CPU 收到中断信息后,需要对中断信息进行处理。而如何对中断信息进行处理,可以由我们编程决定。
我们编写的,用来处理中断信息的程序被称为中断处理程序。一般来说,需要对不同的中断信息编写不同的处理程序。CPU在收到中断信息后,应该转去执行该中断信息的处理程序。
我们知道,若要8086CPU 执行某处的程序,就要将CS:IP指向它的入口(即程序第一条指令的地址)。
可见首要的问题是,CPU在收到中断信息后,如何根据中断信息确定其处理程序的入口?
CPU的设计者必须在中断信息和其处理程序的入口地址之间建立某种联系,使得CPU 根据中断信息可以找到要执行的处理程序。
我们知道,中断信息中包含有标识中断源的类型码。
根据CPU的设计,中断类型码的作用就是用来定位中断处理程序。比如CPU根据中断类型码4,就可以找到4号中断的处理程序。
可随之而来的问题是,若要定位中断处理程序,需要知道它的段地址和偏移地址,而如何根据8位的中断类型码得到中断处理程序的段地址和偏移地址呢?
这就要引入“中断向量表”了。
CPU用8位的中断类型码通过中断向量表找到相应的中断处理程序的入口地址。
那么什么是中断向量表呢?
中断向量表就是中断向量的列表。
那么什么又是中断向量呢?
所谓中断向量,就是中断处理程序的入口地址。
展开来讲,中断向量表,就是中断处理程序入口地址的列表。
中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口,如下图所示:
可以看到,CPU 只要知道了中断类型码,就可以将中断类型码作为中断向量表的表项号,定位相应的表项,从而得到中断处理程序的入口地址。
可见,CPU用中断类型码,通过查找中断向量表,就可以得到中断处理程序的入口地址。
在这个方案中,
一个首要的问题是,CPU如何找到中断向量表?
现在,找到中断向量表成了通过中断类型码找到中断处理程序入口地址的先决条件。
中断向量表在内存中存放,对于8086PC机,中断向量表指定放在内存地址0处。
从内存0000:0000到0000:03FF的1024个单元中存放着中断向量表。
能不能放在别处呢?
不能,如果使用 8086CPU,中断向量表就必须放在0000:0000~0000:03FF单元中,这是规定,因为8086CPU 就从这个地方读取中断向量表。
那么在中断向量表中,一个表项占多大的空间呢?
一个表项存放一个中断向量,也就是一个中断处理程序的入口地址,对于8086CPU,这个入口地址包括段地址和偏移地址,所以一个表项占两个字,高地址字存放段地址,低地址字存放偏移地址。
从上面的讲解中,我们知道,可以用中断类型码,在中断向量表中找到中断处理程序的入口。找到这个入口地址的最终目的是用它设置CS和IP,使CPU执行中断处理程序。用中断类型码找到中断向量,并用它设置CS和IP,这个工作是由CPU的硬件自动完成的。
CPU 硬件完成这个工作的过程被称为中断过程。
CPU 收到中断信息后,要对中断信息进行处理,首先将引发中断过程。硬件在完成中断过程后,CS:IP将指向中断处理程序的入口,CPU开始执行中断处理程序。
有一个问题需要考虑,CPU在执行完中断处理程序后,应该返回原来的执行点继续执行下面的指令。所以在中断过程中,在设置CS:IP之前,还要将原来的CS和IP的值保存起来。
在使用call 指令调用子程序时有同样的问题,子程序执行后还要返回到原来的执行点继续执行,所以,call 指令先保存当前CS和IP的值,然后再设置CS和IP。
下面是 8086CPU在收到中断信息后,所引发的中断过程:
(1)(从中断信息中)取得中断类型码;
(2)标志寄存器的值入栈(保护标志位);
(3)设置标志寄存器的第8位TF和第9位IF的值为0;(这一步的目的以后的内容将介绍)
(4)CS的内容入栈;
(5)IP的内容入栈;
(6)从内存地址为中断类型码*4 和中断类型码 *4+2 的两个字单元中读取中断处理程序的入口地址设置IP和CS。
CPU 在收到中断信息之后,如果处理该中断信息,就完成一个由硬件自动执行的中断过程(程序员无法改变这个过程中所要做的工作)。
中断过程的主要任务就是用中断类型码在中断向量表中找到中断处理程序的入口地址,设置CS和P。因为中断处理程序执行完成后,CPU 还要回过头来继续执行被中断的程序,所以要在设置CS、IP之前,先将它们的值保存起来。可以看到CPU将它们保存在栈中。我们注意到,在中断过程中还要做的一个工作就是设置标志寄存器的TF、IF位,对于这样做的目的,我们将在后面的内容中进行讨论。
因为在执行完中断处理程序后,需要恢复在进入中断处理程序之前的CPU现场(某一时刻,CPU中各个寄存器的值)。所以应该在修改标记寄存器之前,将它的值入栈保存。
我们更简洁的描述中断过程,如下:
(1)取得中断类型码N;
(2) pushf
(3) TF = 0,IF = 0
(4) push CS
(5) push IP
(6)(IP) = (N*4),(CS) = (N*4+2)
在最后一步完成后,CPU 开始执行由程序员编写的中断处理程序。
由于CPU随时都可能检测到中断信息,也就是说,CPU 随时都可能执行中断处理程序,所以中断处理程序必须一直存储在内存某段空间之中。
而中断处理程序的入口地址,即中断向量,必须存储在对应的中断向量表表项中。中断处理程序的编写方法和子程序的比较相似,下面是常规的步骤:
常规的步骤:
(1)保存用到的寄存器。
(2)处理中断。
(3)恢复用到的寄存器。
(4)用 iret 指令返回。
iret指令的功能用汇编语法描述为:
pop IP
pop CS
popf
iret通常和硬件自动完成的中断过程配合使用。
可以看到,在中断过程中,寄存器入栈的顺序是标志寄存器、CS、IP ,而iret的出栈顺序是 IP、CS、标志寄存器,刚好和其对应,实现了用执行中断处理程序前的CPU现场恢复标志寄存器和CS、IP的工作。
iret指令执行后,CPU回到执行中断处理程序前的执行点继续执行程序。
下面的内容中,我们通过对0号中断,即除法错误的中断处理,来体会一下前面所讲的内容。
当CPU执行div等除法指令的时候,如果发生了除法溢出错误,将产生中断类型码为 0 的中断信息,CPU将检测到这个信息,然后引发中断过程,转去执行 0 号中断所对应的中断处理程序。
我们看一下下面程序的执行结果,如下图所示(不同的操作系统下显示可能不同)。
程序:
assume cs:codesg
codesg segment
start:
mov ax,1000h
mov bh,1
div bh
codesg ends
end start
执行结果:
可以看到,当CPU执行div bh
时,发生了除法溢出错误,产生0号中断信息,从而引发中断过程,CPU执行0号中断处理程序。
我们从图中可以看出系统中的0号中断处理程序的功能:显示提示信息“Divide overflow”后,返回到操作系统中。
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。
也可以点点关注,避免以后找不到我哦!
Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!