一篇文章带你搞定中断

1.写在前面

我前面介绍了汇编的一些东西,但是这里我打算重新介绍一下中断,同时通过一个简单的汇编的程序来带你了解中断,同时也会简单的介绍这个的程序怎么一步步的写出来的,这儿我会从8086的CMOS中读取对应的事件,同时也会写一个除法溢出的内中断,来帮助的大家来理解的中断。

2.本篇博客的概述

在这里插入图片描述

3.中断是什么?

中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。这里的意外的情况,就是中断信息。这就好比我们在打字的时候,屏幕上会显示我们的打的字,如果这儿没有中断的,那么我们的打的字,不会立即出现在屏幕上的。还有就是现在比较火的自动驾驶,一辆自动驾驶的车,在路上遇到了特殊的情况,这个时候这辆自动驾驶的车应该立马刹车,这就是中断,不管CPU这个时候在干嘛!都会被打断,去执行刹车的逻辑。但是现在市面上很多自动驾驶的车都做不到,比较拉胯。

4.中断的分类

中断的分类有内中断和外中断,那么怎么理解内中断和外中断?

内中断:顾名思义就是中断信息来自CPU的内部。

外中断:顾名思义就是中断信息来自CPU的外部,响应一些外设的中断。

区别的方式就是中断的信息来自内部还是外部。

5.中断向量表

我们既然已经知道了中断的现象,我们的作为程序员,是不是要根据这个需求来想想这个如何实现的。

我们要确定的中断信息中内容是什么?怎么根据这个中断信息获取到对应的信息,然后根据这个中断信息来获取对应的中断程序,然后进行执行。中断同样也分成可屏蔽中断,不可屏蔽中断,因为CPU执行的任务也会分成紧急不紧急,如果CPU在执行某个很紧急的中断的时候,就好比刹车的中断,这个中断的优先级应该是最高的,这个时候如果来了一个中断,这个时候刹车的中断是不是不能打断,这个时候刹车的中断就是不可屏蔽中断,而能打断的中断就是可屏蔽中断。

我这儿大概的设计了一下中断的过程,上面提到的一个很重要的信息,就是根据中断信息获取到中断程序,这个映射关系有一个东西叫做中断向量表。这个中断向量表中存的有点像java中的map,键就是中断类型码,值就是中断程序的地址。

6.如何书写一个中断

6.1内中断

既然我们上面知道了一个简单的中断过程,就是CPU接收一个中断信息,然后根据这个中断信息,去中断向量表中找到对应的中断程序,然后执行,执行完中断程序后,然后回到原来程序继续执行。

这儿我们第一步就是书写我们的中断程序。具体的汇编代码如下:

assume cs:code,ds:data,ss:stack

data segment
    db 128 dup(0)
data ends

stack segment stack
    db 128 dup(0)
sta ends

code segment
;===========初始化栈=================================
    start:  mov ax,stack
            mov ss,ax
            mov sp,128
;===========初始化栈=================================

            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
show_string:mov al,[si]
            mov es:[di],al
            inc si
            add di,2
            loop show_string
;=============循环显示字符串===========================

            mov ax,4C00H
            int 21H
do0end:nop
;===========除法溢出的中断程序=========================

code ends
end start

上面的汇编的代码主要书写对应的中断的程序,这儿选择用汇编程序来讲的中断的的意思,就是汇编更容易点介绍中断的程序,虽然单片机的也是可以演示对应的中断,但是单片机我没有那么多的硬件,就先用汇编吧,正好最近也是在学汇编。

我们的第一步也是完成了,但是没有设置对应的中断向量表,这个时候就算发生了除法的中断,上面的字符串也不会显示。下面的程序就是设置我们的中断向量表,具体的代码如下:

assume cs:code,ds:data,ss:stack

data segment
    db 128 dup(0)
data ends

stack segment stack
    db 128 dup(0)
stack ends

code segment
;===========初始化栈=================================
    start:  mov ax,stack
            mov ss,ax
            mov sp,128
;===========初始化栈=================================

;===========复制中断程序==============================
            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
;===========复制中断程序==============================

;===========设置中断向量表============================
            cli
			mov ax,0
			mov es,ax
			mov word ptr es:[0*4],200h
			mov word ptr es:[0*4+2],0
			sti
;===========设置中断向量表============================

;===========测试除法溢出=============================
			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

因为8086的CPU的中断向量表在0~200H中,所以这儿我将对应的中断向量改成自己写的中断的程序,这样就实现对应的除法溢出的中断程序,具体的运行的结果如下:

在这里插入图片描述

可以看到的我们一个简单的除法的溢出的中断就实现了。

6.2外中断

我接下来要写的程序就是读取对应的CMOS中的时间,然后通过按键F1来改变显示的颜色,我们先来看下如何显示对应的时间,具体的代码如下:

assume cs:code,ds:data,ss:stack

data segment
    db 256 dup(0)
data ends

stack segment stack
    db 128 dup(0)
stack ends

code segment
        start:  mov ax,stack
                mov ss,ax
                mov sp,128

                call cpy_boot
                
                call sav_old_int9

                mov bx,0
                push bx
                mov bx,7E00H
                push bx
                retf

                mov ax, 4c00H
                int 21H

Boot:
                jmp BOOT_START

TIME_CMOS       db 9,8,7,4,2,0

TIME_STYLE      db 'YY/MM/DD HH:MM:SS',0

BOOT_START:     
                call init_reg
                call clear_screen

                call show_clock

                mov ax,4c00H
                int 21H

show_clock:     call show_style
                call set_new_int9
                mov bx,OFFSET TIME_CMOS - OFFSET Boot + 7E00H
       
showTime:       mov si,bx
                mov di,160*20
                mov cx,6

showDate:       mov al,ds:[si]
                out 70H,al
                in al,71H

                mov ah,al
                shr ah,1
                shr ah,1
                shr ah,1
                shr ah,1
                and al,00001111B

                add ah,30H
                add al,30H 

                mov es:[di],ah
                mov es:[di+2],al

                add di,6
                inc si                  
                loop showDate
                jmp showTime

showtimeRet:    call set_old_int9
                ret

set_old_int9:   push bx
                push es

                mov bx,0
                mov es,bx

                cli
                push es:[200H]
                pop es:[9*4]
                push es:[202H]
                pop es:[9*4+2]
                sti

                pop es
                pop bx
                ret

set_new_int9:
                push bx
                push es

                mov bx,0
                mov es,bx

                cli
                mov word ptr es:[9*4],OFFSET new_int9 - OFFSET Boot + 7E00H
                mov word ptr es:[9*4+2],0
                sti

                pop es
                pop bx
                ret

new_int9:       push ax
                call clear_buff
                in al,60H
                pushf
                call dword ptr cs:[200H]

                cmp al,3BH ;F1
                jne int9Ret
                call change_time_color

int9Ret:        pop ax
                iret

change_time_color:
                push bx
                push cx
                push es

                mov bx,0B800H
                mov es,bx
                mov bx,160*20 + 1

                mov cx,17
                
changeTimeColor:inc byte ptr es:[bx]
                add bx,2
                loop changeTimeColor

                pop es
                pop cx
                pop bx
                ret

show_style:     mov si,OFFSET TIME_STYLE - OFFSET Boot + 7E00H
                mov di,160*20
                call show_string
                ret

clear_buff:     mov ah,1
                int 16H
                jz clearBuffRet
                mov ah,0
                int 16H
                jmp clear_buff

clearBuffRet:   ret

show_string:
                push dx
                push ds
                push es
                push si
                push di

showString:     mov dl,ds:[si]
                cmp dl,0
                je showStringRet
                mov es:[di],dl
                add di,2
                inc si
                jmp showString

showStringRet:  
                pop di
                pop si
                pop es
                pop ds
                pop dx
                ret

clear_screen:
                mov bx,0
                mov dx,0700H
                mov cx,2000

clearScreen:    mov es:[bx],dx
                add bx,2
                loop clearScreen
                ret

init_reg:       
                mov bx,0B800H
                mov es,bx

                mov bx,0
                mov ds,bx
                ret

Boot_end:       nop

sav_old_int9:   
                mov bx,0
                mov es,bx
                push es:[9*4]
                pop es:[200H]
                push es:[9*4+2]
                pop es:[202H]

                ret

cpy_boot:
                mov bx,cs
                mov ds,bx
                mov si,OFFSET Boot

                mov bx,0
                mov es,bx
                mov di,OFFSET 7E00H

                mov cx,OFFSET Boot_end - OFFSET Boot
                cld
                rep movsb

                ret

code ends

end start

上面的代码就是实现一个简单的功能,就是读取CMOS中的时钟的信息,然后按F1按键就可以改变时钟的颜色,现在我来为大家解释没一段代码,首先我们先看start方法中的代码,call cpy_boot,具体的代码如下:

;===============将Boot到Boot_end的指令复制到0000:7E00H处======
cpy_boot:
         mov bx,cs
         mov ds,bx
         mov si,OFFSET Boot

         mov bx,0
         mov es,bx
         mov di,OFFSET 7E00H

         mov cx,OFFSET Boot_end - OFFSET Boot
         cld
         rep movsb

  			 ret

然后我们再看call sav_old_int9,具体的代码如下:

;============将原来的中断保存起来==================
sav_old_int9:   
            mov bx,0
            mov es,bx
            push es:[9*4]
            pop es:[200H]
            push es:[9*4+2]
            pop es:[202H]

            ret

这个时候我们需要看剩下的start中的代码,具体的如下:

mov bx,0
push bx
mov bx,7E00H
push bx
retf

这个时候CPU会将CS:IP变成0000:7E00H这个时候就会执行我们刚刚复制的代码,我们继续往下看

;跳转到 BOOT_START处执行。
jmp BOOT_START

我们继续看BOOT_START中的代码,具体的如下:

call init_reg
call clear_screen
call show_clock

我们先看init_reg方法中的内容具体的如下:

;=======初始化显存的位置==========
init_reg:       
        mov bx,0B800H
        mov es,bx

        mov bx,0
        mov ds,bx
        ret

继续看clear_screen方法中内容具体的如下:

;===========清屏===============
clear_screen:
                mov bx,0
                mov dx,0700H
                mov cx,2000 ;循环的次数

clearScreen:    mov es:[bx],dx
                add bx,2
                loop clearScreen
                ret

最后我们再看show_clock方法中内容具体的如下:

;显示对应的时间
show_clock:     call show_style ;按对应的格式显示对应的时间
                call set_new_int9 ;设置对应新的int9中断程序
                mov bx,OFFSET TIME_CMOS - OFFSET Boot + 7E00H ;设置要取的位置
       
showTime:       mov si,bx
                mov di,160*20
                mov cx,6

showDate:       mov al,ds:[si] ;读取对应的端口位置
                out 70H,al
                in al,71H

;将BCD码转成对应十进制
                mov ah,al
                shr ah,1
                shr ah,1
                shr ah,1
                shr ah,1
                and al,00001111B

                add ah,30H
                add al,30H 

                mov es:[di],ah
                mov es:[di+2],al

                add di,6
                inc si                  
                loop showDate
                jmp showTime

showtimeRet:    call set_old_int9 ;将原来的int9程序复原
                ret

上面的程序就是从CMOS中读取对应的时间,然后设置显示屏幕上,同时我们也设置新的int9中断,当程序执行完了我们将原来的中断复原,我们无法做到完整的写出int9的中断,我们只能借助原来的int9的中断,进行一些对应的调整。我们来看下set_new_int9方法,设置新的中断的程序,具体的代码如下:

set_new_int9:
                push bx
                push es

                mov bx,0
                mov es,bx

								;设置新的中断
                cli
                mov word ptr es:[9*4],OFFSET new_int9 - OFFSET Boot + 7E00H
                mov word ptr es:[9*4+2],0
                sti

                pop es
                pop bx
                ret

new_int9:       push ax
                call clear_buff
                in al,60H
                pushf
                call dword ptr cs:[200H] ;调用老的中断程序

                cmp al,3BH ;F1
                jne int9Ret
                call change_time_color ;是F1直接改变时钟的颜色

int9Ret:        pop ax
                iret

上面的代码设置的我们的新中断,如果按了F1,就直接修改时钟的显示颜色。至此整个程序就讲的差不多了。

6.3中断的流程

在这里插入图片描述

7.写在最后

本篇博客的重点就是中断的执行的流程,至于我写的程序不太重要,重要的是中断的执行流程,我们从中进行对应修改,以达到我们写的CPU能响应我们写的中断,这个时候就需要我们修改对应中断向量表了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值