汇编15:外中断

外中断

可屏蔽中断与不可屏蔽中断

CPU除了能执行指令运算以外,还需要对外部设备进行控制,接受它们的输入,或者向它们输出,也就是I/O能力。CPU通过端口与外部设备进行联系,无论是发送和接受数据,还是输出控制命令,都是先将数据和指令送入相关芯片的端口中,然后再通过芯片对外设进行控制,或者传输到CPU。

外设的输入随时都有可能到达,CPU利用中断机制来及时处理这些信息,这种来自CPU外部的中断信息就被称为外中断。CPU在执行完当前指令后,检测到中断信息,就会引发中断过程,处理外设的输入。

在PC系统中,外中断源一共有以下两类:可屏蔽中断和不可屏蔽中断。

1、可屏蔽中断是CPU可以不响应的外中断,标志寄存器IF决定了CPU能否响应。当CPU检测到可屏蔽中断信息时,如果IF=1,则CPU在执行完当前指令后响应中断,引发中断过程;如果IF=0,则不响应可屏蔽中断。

这也就是为什么在中断时要先把IF位置为0,这是为了在进入中断处理程序后,禁止其他的可屏蔽中断。如果要在中断处理程序中打开处理可屏蔽中断的开关,可以用指令将IF置为1,8086CPU提供如下指令:

sti							设置IF为1
cli							设置IF为0

可屏蔽中断的过程:

(1)通过数据总线将中断类型码n送入CPU中

(2)标志寄存器入栈,IF=0,TF=0

(3)CS和IP入栈

(4)IP=n*4,CS=n*4+2

2、不可屏蔽中断是CPU必须响应的外中断,不可屏蔽中断类型码固定为2,几乎所有由外设引发的外中断都是可屏蔽中断,如键盘输入,不可屏蔽中断是在系统中有必须处理的紧急情况发生时用来通知CPU的中断信息。

不可屏蔽中断的过程:

(1)标志寄存器入栈,IF=0,TF=0

(2)CS和IP入栈

(3)IP=8,CS=0AH

PC机键盘的处理过程

键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上每一个键的开关状态进行扫描。

按下一个键时,开关接通,该芯片就产生一个扫描码,扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60h。松开按下的键时,也会产生一个扫描码,说明了松开的键在键盘上的位置,该扫描码也会被送入60h端口中。

一般将按下一个键时产生的扫描码称为通码,松开一个键时产生的扫描码称为断码。同一个按键的通码和断码的关系:

断码=通码+80h

相同按键的通码的第7位是0,断码的第7位是1。下面是键盘上部分键的通码:

然后键盘的输入到达60h端口后,相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息。检查IF是否为1,如果为1则响应中断,引发中断过程。

中断处理程序的工作如下:

1、读出60h端口中的扫描码

2、如果是字符键的扫描码,就将扫描码和它对应的字符码(ASCII码)送入内存中的BIOS键盘缓冲区;如果是控制键和切换键的扫描码,则将其转变为状态字节写入内存中存储状态字节的单元。

BIOS键盘缓冲区是系统启动后,BIOS用于存放键盘输入的内存区,它可以存储的键盘输入是有限的。

存储键盘状态字节的内存单元是0040:17,它的8位每个位都有特殊的含义:

3、向相关芯片发出应答信息。

案例:显示字母后按esc改变颜色

要求:在屏幕中间依次显示a-z,并可以让人看清,在显示的过程中,按下esc键后,改变显示的颜色。

显示a-z的程序:

assume cs:code 
code segment
start:	mov ax,0b800h
	mov es,ax
	mov ah,'a'							字母从a开始
s:	mov es:[160*12+40*2],ah				                设置显示位置
	inc ah
	cmp ah,'z'							字母到z结束
	jna s								比较指令cmp和jna组合使用意思是不高于则转移
	mov ax,4c00h
	int 21h
code ends
end start

上面这段程序中字母的显示速度太快了,以至于无法看清。我们应该在每显示一个字母后延时一段时间,让人看清后再显示下一个字母,延时的方法就是让CPU执行一段时间的空循环:

	mov dx,10h
	mov ax,0								用dx+ax的方式存储32位循环次数
s:	sub ax,1
	sbb dx,0								循环次数减一
	cmp ax,0									
	jne s
	cmp dx,0								如果ax或dx不为0都要跳转到s处
	jne s

然后我们把这两段程序组合一下,把循环延时的部分组成一个子程序:

assume cs:code

stack segment
	db 128 dup (0)
stack ends

start:	mov ax,stack
	mov ss,ax
	mov sp,128
		
	mov ax,0b800h
	mov es,ax
	mov ah,'a'
s:	mov es:[160*12+40*2],ah				                设置显示位置
	call delay							调用延时子程序
	inc ah
	cmp ah,'z'							字母到z结束
	jna s								比较指令cmp和jna组合使用意思是不高于则转移
		
	mov ax,4c00h
	int 21h
		
delay:	push ax								子程序开始
	push dx
	mov dx,1000h
	mov ax,0
s1:	sub ax,1
	sbb dx,0
	cmp ax,0
	jne s1
	cmp dx,0
	jne s1
	pop dx 
	pop ax
	ret

现在字母已经在屏幕上显示出来了,接下来就是让按下esc键后改变颜色了。我们必须要对原来的9号中断例程进行修改,如果我们要写的中断处理程序称为新的int9中断例程,那么就必须修改中断向量表,但是在新中断例程中又要模拟原来中断例程的工作,所以我们要把原来int9中断例程的程序地址记录下来,在需要调用的时候找到该程序的入口。

有了原来int9的程序入口后,假设入口程序的偏移地址和段地址保存在ds:[0]和ds:[2]中,我们要模仿执行该中断处理程序,因为已经知道中断处理程序的入口,所以无需获得中断类型码,只需要执行下列工作:

1、标志寄存器入栈

2、IF=0,TF=0

3、CS和IP入栈

4、IP=ds*16+0,CS=ds*16+2

综上,调用中断例程的程序如下:

pushf							        标志寄存器入栈

pushf									
pop ax
and ah,11111100b						IF=0,TF=0
push
ax
popf

call dword ptr ds:[0]					        模拟调用子程序,相当于执行CS、IP入栈及其跳转

完整的程序如下:

assume cs:code

stack segment
	db 128 dup (0)
stack ends

data segment
	dw 0,0
data ends

code segment
start:	mov ax,stack
	mov ss,ax
	mov sp,128
		
	mov ax,data
	mov ds,ax
	mov ax,0
	mov es,ax
		
	push es:[9*4]
	pop ds:[0]
	push es:[9*4+2]
	pop ds:[2]					        将原来的int9中断例程的入口地址保存在ds:0和ds:2中
		
	mov word ptr es:[9*4],offset int9
	mov es:[9*4+2],cs			                将中断向量表中的int9中断例程的入口地址更新
		
	mov ax,0b800h
	mov es,ax
	mov ah,'a'
s:	mov es:[160*12+40*2],ah				
	call delay							
	inc ah
	cmp ah,'z'							
	jna s						        显示字母
		
	mov ax,0
	mov es,ax
		
	push ds:[0]
	pop es:[9*4]
	push ds:[2]
	pop es:[9*4+2]				                将改变的中断向量表恢复,否则其他程序将无法使用键盘
		
	mov ax,4c00h
	int 21h						        程序返回
		
delay:	push ax						        延迟子程序
	push dx
	mov dx,1000h
	mov ax,0
s1:	sub ax,1
	sbb dx,0
	cmp ax,0
	jne s1
	cmp dx,0
	jne s1
	pop dx 
	pop ax
	ret
		
int9:	push ax
	push bx
	push es
		
	in al,60h						接受60h端口的扫描码,将其存入al中
		
	pushf
        pushf
	pop ax
	and ah,11111100b						
	push
	ax
	popf
	call dword ptr ds:[0]				        调用原来的int9例程
		
	cmp al,1
	jne int9ret						不等于1即转移到int9ret,只有扫描码是1时才向下执行
		
	mov ax,0b800h
	mov es,ax
	inc byte ptr es:[160*12+40*2+1]		                改变颜色
		
int9ret:pop es
	pop bx
	pop bx
	iret
		
code ends
end start

这个程序因为直接访问硬件,所以必须要在DOS模式下运行。

posted @ 2020-04-30 22:45  勇闯8  阅读(703)  评论(0编辑  收藏  举报