汇编语言(王爽)第十五章 外中断

第十五章

在计算机系统中,CPU除了能执行指令,进行运算外,还能对外部设备进行控制,接收它们的输入,向它们进行输出,即I/O能力,比如在文本编辑器中,我们按键盘中的一个键,可以看到屏幕上出现该字母,是CPU将从键盘上输入的键对应的字符送到显示器上

及时处理外设的输入,有两个问题,即外设的输入随时可能发生,CPU如何得知,以及CPU从何处得到外设的输入

以键盘为例进行讨论

15.1 接口芯片和端口

外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中,CPU向外设的输出也是先送入端口中,再由相关的芯片送到外设,CPU还可以向外设输出控制命令,控制命令也是先送到相关芯片的端口中

CPU通过端口和外部设备进行练习册

15.2 外中断信息

由于外设随时都可能发生需要CPU及时处理的事件,因此CPU提供中断机制,比如外设的输入到达,相关芯片将向CPU发出相应的中断信息吗,CPU在执行完当前指令后,检测到中断信息,引发中断过程,处理外设的输入

PC系统中,外中断源有两类

1、可屏蔽中断

CPU可以不响应,是否响应取决于标志寄存器IF位的设置,当CPU检测到可屏蔽中断信息时,如果IF=1,则CPU在执行完当前指令后响应中断,IF=0则可以屏蔽

可屏蔽中断信息来自于CPU外部,中断类型码通过数据总线送入CPU,而内中断的中断类型码是在CPU内部产生的,可屏蔽中断引发的中断过程除了中断类型码来源不同,其他步骤基本和内中断相同

因此中断过程中将IF设置为0是为了在进入中断处理程序后,禁止其他的可屏蔽中断

如果中断处理程序中需要处理可屏蔽程序,将IF置1

sti	; IF=1
cli	; IF=0

2、不可屏蔽中断

是CPU必须响应的中断,CPU检测到不可屏蔽中断信息时,执行完当前指令后立即响应

8086CPU不可屏蔽中断的中断类型码固定为2,所以中断过程不需要中断类型码

并且最后设置(IP)=(8) (CS)=(0AH)

几乎所有外设引发的外中断都是可屏蔽的(如键盘输入)

不可屏蔽中断是在系统中有必须处理的紧急情况时通知CPU,我们主要讨论可屏蔽中断

15.3 PC机键盘的处理过程

1、键盘输入

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

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

松开按下的键,也产生一个扫描码,说明松开键的位置,该扫描码也被送到60h端口中

按下键时产生的扫描码成为通码,按下时产生的称为断码,扫描码长度为一个字节

断码=通码+80h(通码第7位为0,断码第7位为1)

如:g的通码为22h,断码为a2h

2、引发9号中断

键盘的输入到达60h端口时,相关芯片就会向CPU发出中断类型码为9的可屏蔽中断信息,CPU根据IF的值决定是否响应中断

3、执行int 9中断例程

BIOS提供int 9中断例程,进行基本的键盘输入处理

执行过程:首先读出60h端口中的扫描码,如果时字符键的扫描码,将该扫描码和它对应的ASCII码送入内存中的BIOS键盘缓冲区;如果是控制键(如Ctrl)和切换键(CapsLock)的扫描码,将其转变为状态字节(用二进制位记录控制键和切换键状态的字节)写入内存中存储状态字节的单元,最后对键盘系统进行相关的控制

BIOS键盘缓冲区是系统启动后BIOS用于存放int 9中断例程所接收的键盘输入的内存区

该内存区可以存储15个键盘输入,一个键盘输入占一个字单元,高位存放扫描码,低位存放字符码

0040:17这个内存单元存储键盘状态字节,该字节记录了控制键和切换键的状态,具体各个位记录的信息如下

image-20200327194735576

15.4 编写int9中断例程

编程:在屏幕中间依次显示“a”~“z”,并可以让人看得清在显示过程中,按下Esc键后,改变显示的颜色

如果用普通的循环向一个内存单元依次传送一个字母,由于CPU执行速度很快,所以字母切换太快,无法看清

所以每显示一个字母,让CPU执行一段时间的空循环,用两个16位寄存器存放32位的循环次数

现在需要改变显示的颜色,我们编写的int 9中断例程功能:

首先从60端口读键盘的输出

调用BIOS的int 9中断例程处理其他硬件细节

判断是否为Esc的扫描码

有一个问题在于新的int 9中断例程会改变中断向量表中相应的入口地址,那么我们想调用原来的int 9中断例程就会有麻烦,因此在更改中断向量表中的入口地址前,先保存原有的地址,然后对int指令进行模拟,从而实现对中断例程的调用

int n指令执行时,中断类型码n的作用就是从中断向量表中获取中断例程的入口地址,而这里我们已经提前保存了入口地址,因此只需要标志寄存器入栈,设置IF=0、TF=0,随后CS、IP入栈,最后修改CS、IP的值

我们发现call dword ptr ds:[0]即可完成后两步

最后一个问题是,在程序返回前,需要将中断向量表中的int 9中断例程的入口地址恢复为原来的地址,否则程序返回后,别的程序将无法使用键盘

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]		; 将原来的int 9中断例程的入口地址
			pop ds:[0]			; 保存到ds:0 ds:2单元中
			push es:[9*4+2]
			pop ds:[2]			
			
			mov word ptr es:[9*4],offset int9
			mov es:[9*4+2],cs	; 设置新的int 9中断例程的入口地址
			
			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]			; 该程序不安装
			push ds:[2]			; 只是临时更改int 9中断例程的入口
			pop es:[9*4]		; 在显示字母的过程中只要按下Esc就可以变色
			pop es:[9*4+2]		; 将int 9中断例程的入口恢复为原来的地址
			
			mov ax,4c00h
			int 21h
			
	delay:	push ax
			push dx
			
			mov dx,1000h	; 循环10000000h次
			mov ax,0
	   s1:	sub ax,1
	   		sbb dx,0
	   		cmp ax,0
	   		jne s1
	   		cmp dx,0		; 只有ax dx都不等于0 
	   		jne s1			; 循环才结束
	   		
	   		pop dx
            pop ax
            ret
            
      ; -------------以下为新的int 9中断例程------------
     int9:	push ax
    		push bx
    		push es
    		
    		in al,60h		; 从端口取出扫描码
    		
    		; 调用系统提供的int 9中断例程
    		pushf			; 标志寄存器入栈
    		
    		pushf			
    		pop bx
    		and bh,11111100b	; 设置IF=0 TF=0
    		push bx
    		popf
    		
    		call dword ptr ds:[0] ;相当于CS IP入栈并修改CS IP的值
    		
    		cmp al,1		; 判断是否为Esc键
    		jne int9ret
    		
    		mov ax,0b800h
    		mov es,ax
    		inc byte ptr es:[160*12+40*2+1]	; 属性值加一,改变颜色
    		
   int9ret:	pop es
   			pop bx
   			pop ax
   			iret
	   		
code ends
end start

在模拟int9中断例程时,我门可以精简程序,因为在进入我们写的中断例程中时,IF呵TF都已经置为0,因此在进入BIOS的int 9中断例程时,没必要再设置一次IF和TF

pushf				; 标志寄存器入栈
    		
pushf			
pop bx
and bh,11111100b	; 设置IF=0 TF=0
push bx
popf

call dword ptr ds:[0] ;相当于CS IP入栈并修改CS IP的值

; 精简为
pushf				; 标志寄存器入栈

call dword ptr ds:[0] ;相当于CS IP入栈并修改CS IP的值

还有一个问题,如果在主程序重新设置的int 9中断例程的段地址和偏移地址的指令之间发生了键盘中断,则CPU将转去一个错误的地址执行(CS寄存器中的内容可能会改变)

cli		; 设置IF为0.不响应可屏蔽中断
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs	; 设置新的int 9中断例程的入口地址
sti

15.5 安装新的int 9中断例程

安装一个新的int 9中断例程,在DOS下,按F1键后改变当前屏幕的颜色

assume cs:code

stack segment
	db 128 dup(0)
stack ends

code segment
	start:	mov ax,stack
			mov ss,ax
			mov sp,128
			
			push cs
			pop ds
			
			mov ax,0
			mov es,ax
			
			; 安装
			mov si,offset int9
			mov di,204h
			mov cx,offset int9end-offset int9
			cls
			rep movsb
			
			push es:[9*4]
			pop es:[200h]
			push es:[9*4+2]
			pop es:[20h]
			
			cli
			mov word ptr es:[9*4],204h
			moc word ptr es:[9*4+2],0
			
			mov ax,4c00h
			int 21h
			
	int9:	push ax
			push bx
			push cx
			push es
			
			in al,60h
			
			pushf
			call dowrd ptr cs:[200h]
			
			cmp al,3bh			; F1扫描码为3bh
			
			mov ax,0b800h
			mov es,ax
			mov bx,1
			mov cx,2000
	    s:	inc byte ptr es:[bx]
	    	add bx,2
	    	loop s
	    	
  int9ret:	pop es
  			pop cx
  			pop ax
  			pop ax
  			iret
  			
  int9end:	nop
  
code ends
end start

端口和中断机制是CPU进行I/O的基础

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章