正在加载……
专注、离线、切勿分心
      任何 CPU 都可以在执行完当前正在执行的指令之后,检测到从 CPU 外部发送过来的或内部产生的一种特殊信息,并且可以立即对所接收到的信息进行处理。这种信息就叫做:中断信息

      中断的意思是指:CPU 不在接着(刚执行完的指令)向下执行,而是转去处理这个特殊信息。





内中断的产生
8086CPU,当CPU内部有下面情况发生的时候会产生相应的端信息:
1、除法错误,(执行div指令产生除法溢出)  对应的中断类型码:0
2、单步执行;                                                                                   1
3、执行 int0 指令                                        4
4、执行 int 指令                                         int 后面的参数
8086CPU 用中断类型码的数据来标识中断信息的来源。中断类型码为一个字节型数据,可以表示256种中断信息的来源。产生中断信息的事件,即中断信息的来源,简称为中断源





中断处理程序
      CPU收到中断信息后,需要对中断信息处理,用来处理中断信息的程序被称为中断处理程序
      中断类型码的作用就是来定位中断处理程序。eg:CPU根据中断类型码4,就可以知道4号中断的处理程序。如果要定位中断处理程序,需要知道程序的段地址和偏移地址,要根据8位中断类型码得到中断处理程序的段地址和偏移地址,就要用到 中断向量表。





中断向量表
      中断向量表在内存中保存,其中存放这 256 个中断源所对应的中断处理程序入口地址。只要知道中断类型码,就可以将中断类型码作为中断向量表的表项号,定位相应的表项,从而得到中断处理程序的入口地址。
   中断向量表存放在内存中,8086CPU中,中断向量表指定放在内存地址 0 处。从内存 0000:0000 到 0000:03E8 的 1000 个单元中存放着中断向量表。不能放在别的地方;(这是规定,使用8086CPU,中断向量表就必须放在0000:0000 ~ 0000:03E8 ,8086CPU就从这个地方读取中断向量表;256种中断信息源,256*4=1024个单元,但是根本没有这么多种中断,所以1000个够用了,还用不了这么多因为一个表项存放一个中断向量,也就是一个中断处理程序的入口地址,这个入口地址包含段地址和偏移地址,一个表项占两个字,高地址存放段地址,低地址存放偏移地址。
// 下面这个表就是中断向量表,eg:4号中断源对应的中断处理程序的入口地址为:0070:0008






中断过程
      用中断类型码找到中断向量,并用它设置 CS 和 IP ,这个工作是由 CPU 的硬件自动完成的。CPU 硬件完成这个工作的过程被称为中断过程
      CPU 收到中断信息,先引发中断过程。硬件完成中断过程后,CS:IP 将指向中断处理程序入口,CPU开始执行中断处理程序。
8086CPU在收到中断信息后,引发中断过程:
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)
CPU 在收到中断信息之后,如果处理该中断信息,就完成一个由硬件自动执行的中断过程,程序员无法改变这个过程中所要做的工作。





中断处理程序
      中断随时都可能发生,CPU 随时都可能执行中断处理程序,所以中断处理程序必须一直存储在内存某段空间之中。而中断处理程序的入口地址,即中断向量,必须存储在对应的中断向量表表项中。
中断处理程序的编写方法和子程序的比较相似
1、保存用到的寄存器;
2、处理中断;
3、恢复用到的寄存器;
4、用 iret 指令返回;
iret 指令的功能用汇编语法描述
pop IP
pop CS
popf
iret 指令通常和硬件自动完成的中断过程配合使用,在中断过程中,寄存器入栈的顺序是 标志寄存器、CS、IP,而 iret 的出栈顺序是 IP、CS、标志寄存器。实现了用执行中断处理程序前的CPU现场恢复标志寄存器和CS、IP的工作。iret 指令执行后,CPU 回到执行中断处理程序前的执行点继续执行程序。






除法错误中断的处理
mov ax , 1000h
mov bh , 1
div bh
结果1000h...1,商存储在al中,溢出
执行div bh产生中断,转移到中断处理程序执行,然后执行iret指令返回中断发生的位置。
   //后面进入死循环了






编程处理 0 号中断
assume cs:code
code segment
start:        do0安装程序
        设置中断向量表
        mov ax , 4c00h
        int 21h

do0:        显示字符串"overflow!"
        mov ax , 4c00h
        int 21h
code ends
end start
程序执行时,do0的代码不执行,do0安装程序会把它作为要传送的数据。安装程序先把do0的代码拷贝到内存0:200处,然后设置中断向量表,将do0的入口地址,即偏移地址200h和段地址0,保存在0号表项中,做完这两步后程序就返回。

程序的目的就是在内存0:200处安装do0的代码,将0号中断处理程序的入口地址设置为0:200。do0的代码虽然在程序中,但是不在程序执行的时候执行,只有在除法发生溢出的时候执行中断处理程序才执行。

目的,让0号中断处理我们自己设定的程序; 首先要找一块别的程序不会用到的内存区,可以让计算机自己分配一块,但是我们直接面向硬件资源;然后将 do0 传送到其中即可。
       

   内存0000:0000~0000:03E8,大小约为1KB的空间是系统存放中断处理程序入口地址的中断向量表,8086支持256个中断,但是系统中要处理的中断事件远没有达到256个。所以中断向量表中很多是空的。
   中断向量表是PC系统中最重要的内存区,只用来存放中断处理程序的入口地址,DOS系统和其他应用程序都不会随便使用这段空间。我们可以利用中断向量表的空闲单元来存放我们的程序。一般情况下,从 0000:0200 至 0000:0300 的256个字节的空间所对应的中断向量表都是空的
将 do0 传送到内存 0000:0200 处,同时将这个地址存放在中断向量表的0号表项,do0 的段地址0存放在 0000:0002字单元,偏移地址200h存放在0000:0000字单元






安装
      使用 movsb 指令,将 do0 的代码送入 0:200 处。
      计算程序do0的长度设置 cx ,可以直接计算do0的字节数,但是太麻烦,每次do0内容改变就要重新计算。
assume cs:code
code segment
start:        mov ax , cs    ; 因为cs和code段关联了,所以得到的是code段的起始地址,如果前面还有其他段(比如还定义了datad segment)得到的也是代码段的地址,会不拿到data段的起始地址
        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:        显示字符串"overflow!"
        mov ax , 4c00h
        int 21h
do0end: nop
code ends
end start
"-" 是编译器识别的运算符号,编译器可以用它来进行两个常数的减法。
eg: mov ax , 8-4  ==  mov ax , 4

汇编编译器可以处理的表达式:
eg: mov ax , (5+3)*5/10 == mov ax , 4






do0
assume cs:code
data segment
        db "overflow!"
data ends

code segment
start:        mov ax , cs    ; cs是code段的段地址
        mov ds , ax
        mov si , offset do0
        mov ax , 0
        mov es , ax
        mov di , 200h
        mov cx , offset do0end - offset do0
        cld
        rep movsb
        设置中断向量表
        mov ax , 4c00h
        int 21h
do0:        mov ax , data
        mov ds , ax
        mov si , 0

        mov ax , 0b800h
        mov es , ax
        mov di , 12*160+36*2  ;  在显示缓冲区中间是第12行,一行160个字符,要显示的字符串占9个字符,加上颜色属性一共占18个字符((160-18)%2=71只能从偶数单元开始存放,所以要从72);这里的36*2是刚好72,前半部分可以放40个字符(一字符两字节),中间占一个,所以前半部分40-4=36个字符

        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

这段程序不能正常运行,因为程序执行完后返回,它所占用的内存空间被系统释放,其中存放的“overflow!”也有可能被别的信息覆盖。而 do0 程序被放到了 0:200 处,随时都会因发生了除法溢出而被 CPU 执行,所以程序可能显示不出指定字符串。
assume cs:code
code segment
start:        mov ax , cs
        mov ds , ax
        mov si , offset do0
        mov ax , 0
        mov es , ax
        mov di , 200h
        mov cx , offset do0end-offset do0
        cld
        rep movsb
        设置中断向量表
        mov ax , 4c00h
        int 21h

do0:        jmp short do0start
        db "overflow!"

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
s:        mov al , [si]
        mov es:[di] , al
        inc si
        add di , 2
        loop s

        mov ax ,4c00h     ; 这两句代码用来返回,其实写 iret 是最好的
        int 21h
do0end:        nop
        code ends
end start

我们将"overflow!"放到do0程序中,执行第一次执行时会将标号do0到标号do0end之间的内容送到0000:0200处。
因为do0开始处必须要是一条可执行代码,所以放上jmp指令转移到正式的do0程序。当除法发生溢出的时候,CPU执行0:200处的jmp指令,跳过后面的字符串,转到正式的do0程序执行。
do0程序执行的过程中必须要找到"overflow!",它和do0的代码段处于同一个断中,当发生溢出执行中断程序,CS中必然存放着do0的段地址0,jmp指令占两个字节,所以"overflow!"的偏移地址202h






设置中断向量
      将do0的入口地址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






单步中断
    CPU在执行完一条指令之后,如果检测到标志寄存器的TF位为1,则产生单步中断,引发中断过程。单步中断的中断类型码为1,它所引发的中断过程:
1、取得中断类型码1;
2、标志寄存器入栈,TF、IF设置为0;
3、CS、IP入栈;
4、(IP)=(1*4),(CS)=(1*4+2)。
如果 TF=1,则执行一条指令后,CPU就要转去执行1号中断处理程序。

debug程序 -t 命令利用CPU提供的单步中断功能,执行完一条指令,调用中断处理程序打印所有寄存器的内容后等待输入命令。
使用 T 命令,debug将TF设置为1,使得CPU在工作于单步中断方式下,则在 CPU 执行完一条指令后引发单步中断,转去执行中断处理程序;中断处理程序也是由一条条指令组成的,如果在执行中断处理程序之前,TF=1,则在CPU执行完中断处理程序的第一条指令后,又要产生单步中断,则又要转去执行单步中断的中断处理程序,陷入死循环。
CPU在进入中断处理之前,设置TF=0,避免陷入死循环;中断过程
1、取得中断类型码 N;
2、标志寄存器入栈,TF=0、IF=0;
3、CS、IP 入栈;
4、(IP)=(N*4),(CS)=(N*4+2)

CPU 提供单步中断功能的原因就是:为单步跟踪程序的执行过程,提供了实现机制。





响应中断的特殊情况
      有些情况,CPU在执行完当前指令后,即便发生中断,也不会响应。
eg: 执行完设置 ss 指令后,CPU响应中断,引发中断过程,要在栈中压入标志寄存器值和 CS、IP;ss 改变,sp 并未改变,ss:sp 指向的不是正确的栈顶,将引起错误。所以CPU在执行完设置 ss 的指令后,不响应中断。
                




posted on 2017-11-25 19:21  正在加载……  阅读(341)  评论(0编辑  收藏  举报