• 第十二章 内中断


    引言

    本书主要讲解硬件中断。

    12.1 内中断的产生

    12.2 中断处理程序

    12.3 中断向量表

    • 中断向量表在内存中存放,对于8086PC机,中断向量表指定存放在内存地址0处。
    • 从内存0000:0000到0000:03FF的1024个单元中存放着中断向量表。为什么是1024个字节呢?我们回忆,段地址和偏移地址分别都是16位的,它俩经过组合构成一个20位的物理地址。16位相当于2个字节,我们要得到物理地址就要分别存放段地址和偏移地址,所以一个物理地址需要4个字节(也就是4个内存单元)来存放,因为中断向量表中有256个中断,一个中断对应一个物理地址,所以需要256*4=1024个字节,即1024个内存单元。
    • 我们在第五章有讲,内存0000:00000000:03FF,大小为1KB的空间是系统存放中断处理程序入口地址的中断向量表。一般情况下,从0000:02000000:02FF(这是256个字节,即256个内存单元)的256个字节的空间所对应的中断向量表项都是空的,操作系统的其它应用程序都不占用。

    12.4 中断过程

    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开始执行由程序员编写的中断处理程序。

    动画演示中断过程(13:45处)

    12.5 中断处理程序和iret指令

    中断处理程序,常规的步骤:

    1. 保存用到的寄存器;
    2. 处理中断;
    3. 恢复用到的寄存器;
    4. 用 iret 指令返回。

    iret 指令的功能用汇编语法描述为:

    pop IP
    pop CS
    popf
    

    iret 通常和硬件自动完成的中断过程配合使用。可以看到,在中断过程中,寄存器入栈的顺序是标志寄存器、CS、IP,而 iret 的出栈顺序是IP、CS、标志寄存器,刚好和入栈对应,实现了用执行中断处理程序前的CPU现场恢复标志寄存器和CS、IP的工作。iret 指令执行后,CPU回到执行中断处理程序前的执行点继续执行程序。

    12.6 除法错误中断的处理

    除法溢出对应0号中断。

    我们编写程序 test.asm 测试除法溢出:

    assume cs:codesg
    
    codesg segment
    start:
            mov ax,1000h    ;被除数1000
            mov bh,1    ;除数1
            div bh  ;bh决定了该除法为8位除法
            ;商寄存器 al 为 8 位,存储无符号数除法所得商范围 0~255
            ;1000/1=1000,很明显在 al 中无法存放,引发除法溢出
    
    codesg ends
    end start
    

    div 指令可以做除法。当进行 8 位除法的时候,用 al 存储结果的商,ah 存储结果的余数;进行 16 位除法的时候,用 ax 存储结果的商,dx 存储结果的余数。

    test.asm 编译,链接和Debug截图:

    屏幕快照 2018-10-15 16.12.28

    屏幕快照 2018-10-15 16.13.08

    可以看到引发除法溢出 Divide overflow。

    8086除法指令DIV,IDIV

    12.7 编程处理0号中断

    12.8 安装

    更详细的程序框架:

    assume cs:code
    
    code segment
    start:
    	;设置ds:si指向源地址
        ;设置es:di指向目的地址
        ;设置cx为传输长度
        ;设置传输方向为正
        rep movsb
    
        ;设置中断向量表
    
        mov ax,4c00h
        int 21h
        
    do0:
    	;显示字符串"Welcome to Fishc.com!"
    	
    	mov ax,4c00h
    	int 21h
    
    code ends
    end start
    

    更明确的的程序:

    assume cs:code
    
    code segment
    start:
    	;设置ds:si指向源地址
        mov ax,cs
        mov ds,ax
        mov si,offset do0
    
    	;设置es:di指向目的地址
        mov ax,0
        mov es,ax
        mov di,200h
    
    	;设置cx为传输长度
        mov cx,do0部分代码的长度
    
    	;设置传输方向为正
        cld
    
        rep movsb
    
        ;设置中断向量表
    
        mov ax,4c00h
        int 21h
    
    do0:
        ;显示字符串"Welcome to Fishc.com!"
    	
    	mov ax,4c00h
    	int 21h
    
    code ends
    end start
    

    接着就是计算do0部分代码的长度:

    assume cs:code
    
    code segment
    start:
    	;设置ds:si指向源地址
        mov ax,cs
        mov ds,ax
        mov si,offset do0
    
    	;设置es:di指向目的地址
        mov ax,0
        mov es,ax
        mov di,200h
    
    	;设置cx为传输长度
        mov cx,offset do0end - offset do0
    
    	;设置传输方向为正
        cld
    
        rep movsb
    
        ;设置中断向量表
    
        mov ax,4c00h
        int 21h
    
    do0:
        ;显示字符串"Welcome to Fishc.com!"
    	
    	mov ax,4c00h
    	int 21h
    do0end: nop
    
    code ends
    end start
    

    接下来是do0程序,do0程序的主要任务是显示字符串,程序如下:

    do0:;显示字符串"Welcome to Fishc.com!"
        mov ax,0b800h
        mov es,ax
        mov di,12*160+36*2  ;设置es:di指向显存空间的中间位置
        mov cx,21    ;设置cx为字符串长度
    
    s:  ;把字符串中的字符一个一个的拷贝过去
        mov al,[si]
        mov es:[di],al
        inc si
        add di,2
        loop s
    	
    	mov ax,4c00h
    	int 21h
    do0end: nop
    

    现在,我们得到了相对完整的汇编程序版本 program1.asm

    assume cs:code
    
    data segment
        db "Welcome to Fishc.com!"
    data ends
    
    code segment
    start:
    	;设置ds:si指向源地址
        mov ax,cs
        mov ds,ax
        mov si,offset do0
    
    	;设置es:di指向目的地址
        mov ax,0
        mov es,ax
        mov di,200h
    
    	;设置cx为传输长度
        mov cx,offset do0end - offset do0
    
    	;设置传输方向为正
        cld
    
        rep movsb
    
        ;设置中断向量表
    
        mov ax,4c00h
        int 21h
    
    do0:;显示字符串"Welcome to Fishc.com!"
        mov ax,0b800h
        mov es,ax
        mov di,12*160+36*2  ;设置es:di指向显存空间的中间位置
        mov cx,21    ;设置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
    

    程序 program1.asm 看似合理,可实际上却大错特错。

    注意,“Welcom to Fishc.com!”在程序 program1 的data段中。程序 program1 执行完成后返回,它所占用的内存空间被系统释放,而在其中存放的“Welcom to Fishc.com!”也将很可能被别的信息覆盖。而do0程序被放到了0000:0200处,随时都会因发生了除法溢出而被CPU执行,很难保证do0程序从原来程序 program1 所处的空间中取得的是要显示的字符串“Welcom to Fishc.com!”,因为字符串“Welcom to Fishc.com!”是存放在数据段的,随时可能被覆盖,不是一段安全的内存空间。因为 program1 执行完,它的数据段就被释放了。

    故,由于do0程序随时可能被执行,而它要用到字符串“Welcom to Fishc.com!”,所以该字符串也应该存放在一段不会被覆盖的空间中。解决思路是把字符串存放到0000:0200处,也就是do0程序在内存中的地址0000:0200处。

    do0:    ;显示字符串"Welcome to Fishc.com!"
            jmp short do0start  ;执行到此处,CPU直接跳到do0start执行
            db "Welcome to Fishc.com!"  ;在代码段里存放数据。这算是“歪门邪道”
    do0start:
            mov ax,0b800h
            mov es,ax
            mov di,12*160+36*2  ;设置es:di指向显存空间的中间位置
    

    这样修改后,我们的字符串就能跟随do0保存在安全空间中,但是问题又来了。do0程序执行过程中必须找到“Welcom to Fishc.com!”,那么它在哪里呢?

    首先来看段地址,“Welcom to Fishc.com!”和do0的代码处于同一个段中,而除法溢出发生时,CS中必然存放do0的段地址,也就是“Welcom to Fishc.com!”的段地址;再来看偏移地址,0000:0200处的指令为 jmp short do0start ,这条指令占两个字节,所以“Welcom to Fishc.com!”的偏移地址为0202h。

    最后,我们将do0的入口地址0000:0200写入中断向量表的0号表项中,使do0成为0号中断的中断处理程序。0号表项的地址为0000:0000,其中0000:0000字单元存放偏移地址;0000:0002字单元存放段地址。

    完整程序实现 program2.asm

    assume cs:code
    
    code segment
    start:
    	    ;设置ds:si指向源地址
            mov ax,cs
            mov ds,ax
            mov si,offset do0
    
    	    ;设置es:di指向目的地址
            mov ax,0
            mov es,ax
            mov di,200h
    
    	    ;设置cx为传输长度
            mov cx,offset do0end - offset do0
    
    	    ;设置传输方向为正
            cld
            rep movsb
    
            ;设置中断向量表
            mov ax,0
            mov es,ax
            mov word ptr es:[0*4],200h
            mov word ptr es:[0*4+2],0
    
            mov ax,4c00h
            int 21h
    
    do0:    ;显示字符串"Welcome to Fishc.com!"
            jmp short do0start  ;执行到此处,CPU直接跳到do0start执行
            db "Welcome to Fishc.com!"  ;在代码段里存放数据。这算是“歪门邪道”
    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,21    ;设置cx为字符串长度
    
    s:      ;把字符串中的字符一个一个的拷贝过去
            mov al,[si]
            mov es:[di],al
            inc si
            add di,1
            mov al,02h  ;设置颜色
            mov es:[di],al
            add di,1
            loop s
    	
    	    mov ax,4c00h
    	    int 21h
    do0end: nop
    
    code ends
    end start
    

    rep movs汇编指令的问题

    当我们的程序 program2.asm 执行过后,另外一个程序触发了0号中断,就会显示出字符串“Welcom to Fishc.com!”,而不是除法溢出提示。

    • 实际演示截图

      1. 在没有运行 program2.exe 前,debug除法溢出程序 test.exe 单步跟踪提示除法溢出 Divide overflow。编译、链接 program2.asm

        屏幕快照 2018-10-15 16.47.16

      2. 运行 program2.exe 之后,再 debug test.exe

        屏幕快照 2018-10-15 16.49.26

    12.9 do0

    12.10 设置中断向量

    12.11 单步中断

    标志寄存器的 TF 位为 1,则CPU产生单步中断。

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

    12.12 相应中断的特殊情况

  • 相关阅读:
    找到数组或整数列表中连续子序列的最大和
    编写一个调用的函数,该函数接受一个括号字符串,并确定括号的顺序是否有效
    SRS流媒体服务器搭建及拉取摄像头视频流经opencv处理后再推流至SRS
    (pymysql.err.OperationalError) (1055, "Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column
    微信商户转帐到个人零钱
    双色球1千万,等你来拿!
    python后端开发面试总结
    alipay接入步骤
    Mongodb简单操作
    flask基础
  • 原文地址:https://www.cnblogs.com/hacker-x/p/9792037.html
Copyright © 2020-2023  润新知