汇编笔记_第十二章
内中断
中断的产生
中断:
中断分为 软件中断 和 硬件中断,前者又称为内中断后者又称为外部中断;
软件中断: 由CPU内部的某些事件引起的,不受中断允许标志IF的控制。包括:
- 由终端指令
int n
引起,n指出中断类型 - 由CPU的某些错误引起,除法错中断(类型号0)、溢出中断(类型号4)
- 为调试程序Debug设置的中断,单步中断(类型号1)、断点中断(类型号3)
硬件中断: 由输入输出外设产生的中断请求引起的中断。
8086系统的硬件中断可分为 可屏蔽中断 和 不可屏蔽中断。所有的中断请求都有对应的中断处理子程序与之对应;
补充:
- 内中断中,int n和into指令产生的中断以及出发错中断都不能被禁止,并且比任何外部中断的优先级都高;
- 8086系统的硬件中断都是通过CPU的引脚引入中断请求型号,不可屏蔽中断请求信号接到CPU的NMI引脚上,当发生电源故障,奇偶检验错,io通道校验错等紧急情况时有系统自动产生,NMI不可屏蔽中断的类型号为2;
- 可屏蔽中断是键盘、显示器、打印机、磁盘、串口、并口等外设发出的。由于可屏蔽中断种类较多,系统专门有8259A中断控制器来管理这些中断。可屏蔽中断指这些外设可以用软件设置允许或禁止器发出中断请求。8086可屏蔽中断的中断类型号为08H~0FH;
- 优先级由高到低:内部中断-非屏蔽中断-可屏蔽中断-单步中断;
- 硬件中断时,CPU执行完一条指令后都会读取INTR引脚信号;
- 软件中断时,若为0-4号中断,CPU按相应的方式处理,如果是int n指令,则由n获取中断向量转而执行中断处理程序;
- 单步中断是int 1(TF=1);
中断处理程序
CPU的设计者必须在中断信息和其处理程序的入口地址之间建立某种联系,使得CPU可以根据中断信息可以找到哟啊执行的处理程序;
中断类型和中断向量表
8086提供了256个中断类型,类型号为0~FFH;
CPU用8位的中断类型码通过中断向量表找到相应的中断处理程序的入后地址;
用中断类型码找到中断向量,并用它设置CS和IP,这个工作是由CPU的硬件自动完成的,这个过程称为 中断过程;
8086中专门建立了一张中断向量表用于保存所有的中断向量。表位于内存最低地址区0开始的1KB单元。每个中断向量占用4个字节,高位放段地址,低位放偏移地址;
中断向量表的地址为:00000H~003FCH;
其中,
- 5个专用中断:类型0中断(出发出错)00000H00003H,保存ip和cs内容、类型1中断(单步中断)00004H00007H、类型2中断(NMI)00008H0000BH、类型3中断(断点中断)0000CH0000FH、类型4中断(溢出中断)00010H~00013H;
- 27个系统保留中断:类型5中断类型31中断00014H0007FH;
- 224个用户自定义中断: 类型32中断类型255中断00080H003FCH;
中断过程
8086CPU的中断过程:
1、取得中断类型码
2、标志寄存器的指入栈
3、设置标志寄存器的第8位TF和第9位IF的值0
4、CS的内容入栈
5、IP的内容入栈
6、从内存地址位中断类型码*4和中断类型码*4+2的两个字的单元中读取中断处理程序的入口地址设置IP和CS
- 在使用call指令调用子程序时有同样的问题,子程序执行后还要返回到原来的执行点继续执行,所以,call指令先保存当前CS和IP值,然后设置CS和IP跳转;
中断处理程序和iret指令
CPU要随时都可能执行中断程序,所以中断处理程序必须一直在内存的某段空间中;同时,中断程序的入口地址,即中断向量必须储存在对应的中断向量表表项中;
中断处理程序的编写方法:
- 1、保存用到的寄存器
- 2、处理中断
- 3、恢复用到的寄存器
- 4、用
iret
指令返回
iret指令的功能用汇编语法描述为:
pop IP
pop CS
popf
iret通常和硬件自动完成的中断程序配合使用;
入栈的顺序时标志寄存器、CS、IP,和iret的出栈相反;
编写处理0号中断
当发生出发溢出时,CPU将进行以下工作:
- 1、取得中断类型码0
- 2、标志寄存器入栈,TF、IF设置为0
- 3、CS、IP入栈
- 4、(IP)=(0*4),(CS)=(0*4+2)
中断处理程序do0,只需显示"overflow!"
- 1、相关处理
- 2、向显示缓冲区送入字符串"overflow!"
- 3、返回dos
因为除法溢出随时可能发生,CPU随时可能将cs:ip指向do0的入口,执行程序,所以为了简单可以将中断处理程序do0放到中断向量表的0000:0200处;
所以最后的处理是;
- 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
;do0程序在这段程序中并不执行,只是作为数据传送到中断向量表中,即安装程序
code ends
end start
安装
中断程序的安装可以使用 rep movsb
来将do0的代码送入到0000:0200处;
大致框架:
assume cs:code
code segment
start:
设置es:di指向目的地址
设置ds:si指向源地址
设置cx的长度
设置传送方向为正
rep movsb
设置中断向量表
mov ax,4c00h
int 21h
do0:
显示字符串:"overflow!"
mov ax,4c00h
int 21h
code ends
end start
使用 rep movsb
要确定的信息:
- 目的地址:0000:0200
- 源地址:code:offset do0
- 传送的长度cx: do0的代码的长度
- 传送方向为正:
cld
因为do0的长度根据不同的序求需求会改变,没有必要每一次都计算,可以在最后加一个 do0end:nop
,这样 mov cx,offset do0end-offset do0
即可,"-"是编译器识别的运算符号,编译器可以用它来进行两个 常数 的减法,同时,也可以处理表达式;
最后的安装程序:
assume cs:code
code segment
start:
mov ax,0
mov es,ax
mov di,0200h ;目的地址
mov ax,cs
mov ds,ax
mov si,offset do0 ;源地址
mov cx,offset do0end-offset do0
cld
rep movsb
设置中断向量表
mov ax,4c00h
int 21h
do0:
显示字符串:"overflow!"
mov ax,4c00h
int 21h
do0end:nop
code ends
end start
do0
do0的任务是显示字符串,大致的框架:
do0:
设置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
这里的字符串的存放不能像之前的程序那样放在总程序的数据段中,因为当总程序运行完后数据段也就释放,不能保证中断程序所显示的字符串还是"overflow!";
所以要将字符串放在do0中,将其以通送到中断向量表的0000:0200处;
这样在发生除法溢出时,cs中存放的是do0的段地址,同时也是字符串的段地址,而因为程序要跳过字符串,不把他当作代码执行,所以在字符串之前要有一个jmp指令跳过字符串到实际的代码处,所以字符串的偏移地址就为0000:0200加2即0000:0202处(jmp占两个字节)
最后的程序:
do0:
jmp short do0start
db "overflow!"
do0start:
mov ax,cs
mov ds,ax
mov si,0202h
;设置ds:si指向字符串
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
设置中断向量
设置中断向量即为将do0的入后地址0000:0200写入到中断向量表的0号表项中,使do0成为0号中断的中断处理程序;
0号表项的地址为0:0,其中0:0字单元存放偏移地址(ip),0:2字单元存放段地址(cs),
mov ax,0
mov es,ax
mov word ptr es:[0*4],0200h
mov word ptr es:[0*4+2],0
综上,最后的程序如下:
assume cs:code
code segment
start:
mov ax,0
mov es,ax
mov di,0200h
mov ax,cs
mov ds,ax
mov si,offset do0
mov cx,offset do0end-offset do0
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[0*4],0200h
mov word ptr es:[0*4+2],0
mov ax,1000h
mov bh,1
div bh
mov ax,4c00h
int 21h
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
单步中断
CPU在执行完一条指令后,如果检测到标志寄存器的TF位为1,则产生单步中断,单步中断的中断类型码为1;
他引发的中断类型如下:
- 1、取得中断类型码1
- 标志寄存器入栈,TF、IF置0
- CS,IP入栈
- (IP)=(14),(CS)=(14+2)
使用t命令时,debug将TF设置为1,使得cpu执行完这条指令后引发单步中断;
执行单步中断的中断程序,所有寄存器的内容显示,等待输入;
中断处理程序也是由一条条指令组成的,如果在执行中断处理程序之前,TF=1,则CPU在执行完中断处理程序的第一条指令后,又要产生单步中断,则又要转去执行单步中断的中断处理程序…… ?
解决方法是在进入中断程序之前设置TF=0,也就是在前面说的中断过程中的第二步标志寄存器入栈后设置TF=0的原因;
CPU提供单步中断功能的原因就是,为单步跟踪的执行过程,提供了实现机制。
响应中断的特殊情况
有些情况下,CPU在执行完当前指令后,即使发生中断,也不相应,例如:在执行完向ss寄存器传送数据的指令后,发生中断时,CPU也不会响应,因为,ss:sp联合指向栈顶,对他们的设置应该连续完成;
如果在执行完设置ss的指令后,CPU响应中断,引发中断过程,要在栈中压入标志寄存器、CS和IP的值。
而ss改变,sp并未改变,ss:sp指向的不是正确的栈顶,将引起错误。
所以CPU在执行完设置ss的指令后,不响应中断。
这给连续设置 ss和sp,指向正确的栈顶提供了一个时机。
即,我们应该利用这个特性,将设置ss和sp的指令连续存放,使得设置sp的指令紧接着设置ss的指令执行,而在此之间,CPU不会引发中断过程
解释了实验2中的(3)
Debug 利用单步中断来实现T命令的功能,
也就是说,用T命令执行一条指令后,CPU响应单步中断,执行Debug设置好的处理程序,才能在屏幕上显示寄存器的状态,并等待命令的输入。
而在mov ss,ax指令执行后,CPU根本就不响应任何中断,其中也包括单步中断,
所以Debug设置好的用来显示寄存器状态和等待输入命令的中断处理程序根本没有得到执行,所以我们看不到预期的结果。
CPU接着向下执行后面的指令mov sp,10h,然后响应单步中断,我们才看到正常的结果
(以上引用来自汇编老师的课件,转载请注明地址)
https://www.cnblogs.com/31415926535x/p/10206507.html
(end)