汇编-12.0-内中断
任何一个通用CPU,都具备可以在执行完当前正在执行的指令后,检测从CPU外部发送过来的或内部产生的
中断信息。
中断信息可以来自CPU的内部和外部。
1.内中断的产生
(1).除法错误(中断类型码:0),如:执行div指令产生的除法溢出;
(2).单步执行(中断类型码:1);
(3).执行into指令(中断类型码:4);
(4).执行int指令(中断类型码:int n,指令中的n为字节型立即数,是提供给CPU的中断类型码)。
2.中断处理程序
CPU在收到中断信息后,应该转去执行该中断的处理程序.
若要CPU执行某处程序,就要将CS:IP指向它的入口(即程序的第一条指令地址).
中断类型码的作用就是用来定位中断处理程序.
3.中断向量表
CPU用8位的中断类型码通过中断向量表找到相应的中断处理程序的入口地址.
中断向量,就是中断处理程序的入口地址.
中断向量表,就是中断处理程序入口地址的列表.
中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口.
只要得到了中断类型码,就可以将中断类型码作为中断向量表的表项号,定位相应的表项,从而
得到中断处理程序的入口地址.
4中断过程
用中断类型码找到中断向量,并用它设置CS和IP,这个工作是由CPU的硬件自动完成的.
CPU硬件完成这个工作的过程被称为中断过程.
CPU收到中断信息后,要对中断信息进行处理,首要将引发中断过程,硬件完成中断过程后,
CS:IP将指向中断处理程序的入口,CPU开始执行中断处理程序.
在中断设置CS:IP之前,还要将原来的CS和IP的值保存起来.在使用call指令调用子程序有同样的问题.
子程序执行完毕后,还要返回原来的执行点继续执行,所以call指令先保存当前CS和IP的值.
CPU收到中断信息后,引发中断过程:
(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)=(N*4),(CS)=(N*4+2)
最后执行由程序员编写的中断处理程序;
5.中断处理程序和iret指令
由于CPU随时都可能检测到中断信息,随时都有可能执行中断处理程序,所以中断处理程序必须一直存储在内存某段空间中。
而中断程序的入口地址,即中断向量,必须存储在对应的中断向量表表项中。
常规步骤:
(1).保存用到的寄存器;
(2).处理中断;
(3).恢复用到的寄存器;
(4).用iret指令返回。
iret指令功能为:
pop IP pop CS popf
iret通常和硬件自动完成的中断过程配合使用。
在中断过程中,寄存器入栈的顺序是标志寄存器、CS、IP、,而iret的出栈顺序是IP、CS、标志寄存器。
6.除法错误中断的处理
当CPU执行div除法指令的时候,如果发生了溢出错误,将产生中断类型码为0的中断信息,CPU将检测到这个信息,
然后引发中断过程,转去执行0号中断所对应的中断处理程序。
7.编程处理0号中断
中断程序框架:
assume cs:code code segment start: do0安装程序 设置中断向量表 mov ax,4c00h int 21h do0: 显示字符串“xxxxx” mov ax,4c0h int 21h code ends end start
程序分为两部分:
(1).安装do0,设置中断向量的程序.;
(2).do0.
8.安装
可以使用movsb指令,将do0的代码送入0:200处.
assume cs:code code segment start: 设置es:di指向目的地址 设置ds:si指向源地址 设置cx为传输长度 设置传输方向为正 rep movsb 设置中断向量表 mov ax,4c00h int 21h do0:显示字符串“xxxxxxx” mov ax,4c00h int 21h code ends end start
rep movsb指令要确定的信息:
(1).传送的原始位置,段地址:code,偏移地址:offset do0;
(2).传送的目的地址:0:2000;
(3).传送的长度:do0部分代码长度;
(4).传送的方向:正向(标志寄存器DF位决定).
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:显示字符串“xxxxxxx” mov ax,4c00h int 21h code ends end start
确定do0代码长度,计算一下do0所有指令码的字节数
还可以利用编辑器来计算do0的代码长度.
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:显示字符串“xxxxxxx” mov ax,4c00h int 21h code ends end start
"-"是编辑器识别的运算符号,编辑器可以用它来进行两个常数的减法.
9.do0
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 do0-offset do0 ;设置cx为传输长度 cld rep movsb 设置中断向量表 mov ax,4c00h int 21h do0:jmp short do0start db"xxxxxxxxx" 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
注意:do0程序开始处的"xxxxxxxxxx"不是可执行代码,所以在"xxxxxxx"前加上一条jmp指令,转到正式的do0程序.
当除法溢出时,CPU执行0:200处的jmp指令,跳过后面的字符串,转到正式的do0程序执行.
"xxxxxxx"和do0的代码处于同一个段中,而除法溢出发生时,CS中必然存放do0的段地址,也就是"xxxxxxxx"的段地址,
0:200处的指令为jmp short do0start,这条指令占两个字节,所以"xxxxxxxx"的偏移地址为202h.
10.设置中断向量
将do0的入口地址0:200,写入0:200,写入中断向量表的0号表项中,使do0成为0号中断的中断处理程序.
0号表项的地址为0:0,其中0:0字单元存放偏移地址,0:2单元存放段地址.
mov ax,0 mov es,ax mov word ptr es:[0*4],200h mov word ptr es:[0*4+2],0
11.单步中断
CPU在执行完一条指令之后,如果检测到标志寄存器的TF位为1,则产生单步中断,引发中断过程.
单步中断的中断类型码为1:
(1).取得中断标志类型码1;
(2).标志寄存器入栈,TF,IF设置为0;
(3).CS,IP入栈;
(4).(IP)=(1*4),(CS)=(1*4+2).
在进入中断处理程序之前,设置TF=0,避免了在执行中断处理程序的时候发生单步中断的循环.
(1).取得中断标志类型码N;
(2).标志寄存器入栈,TF,IF设置为0;
(3).CS,IP入栈;
(4).(IP)=(N*4),(CS)=(N*4+2).
cld指令:将标志寄存器df位置0
std指令:将标志寄存器的df位置1
12.响应中断的特殊情况
在执行完向ss寄存器传送数据的指令后,即便是发生中断,CPU也不会响应.因为SS:IP联合指向栈顶,
而对它们的设置是连续完成的.如果在执行完设置ss的指令后,CPU响应中断,引发中断过程,要在栈中压入标志寄存器,
CS,IP的值.而ss改变,sp并未改变,ss:sp指向的不是正确的栈顶,引起错误.所以CPU在执行设置ss后,不响应中断!
利用这个特性,将设置SS和SP的指令连续存放,使得设置sp的指令紧接设置ss指令执行,在此之间,CPU不会引发中断过程.