汇编语言随笔(13)- 外中断(可屏蔽中断)、实验15

端口的作用

      在PC系统的接口卡和主板上,装有各种接口芯片。这些外设接口芯片内部有若干寄存器,CPU将这些寄存器当作端口来访问。
      外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中;CPU向外设的输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。CPU还可以向外设输出控制命令,这些命令也是先送到相关芯片的端口中,再由相关芯片根据命令对外设实施控制。

外中断

      之前所提到的中断类型都是属于内中断,也就是在CPU的内部有需要处理的事情时,产生中断信息,引发中断过程。
      在此,我们主要介绍外中断,也就是说当CPU的外部有需要处理的事情时,比如:外设的输入到达,相关芯片将向CPU发出相应的中断信息。CPU在执行完当前指令后,可以检测到发送过来的中断信息,引发中断过程来处理外设的输入。

      外中断源一共有两类:
(1)可屏蔽中断
      这类中断是CPU可以不响应的外中断。CPU是否响应可屏蔽中断,在于标志寄存器IF位的设置。当CPU检测到可屏蔽中断信息时,如果IF=1,则CPU在执行完当前指令后响应中断,引发中断过程;如果IF=0,则不响应可屏蔽中断。
      故:中断过程中的置TF=0是为了在中断处理程序中防止CPU陷入单步执行(内中断)循环;而IF=0是为了在中断处理程序中禁止其他的可屏蔽中断(默认中断不能嵌套)
      8086CPU提供的设置IF的指令如下:sti,设置IF=1;cli,设置IF=0。
(2)不可屏蔽中断
      这类中断是CPU必须响应的外中断。当CPU检测到不可屏蔽中断信息时,则在执行完当前指令后,立即响应引发中断过程。 对于8086CPU,不可屏蔽中断的中断类型码固定为 2,所以在中断过程中不需要取中断类型码。

      几乎所有由外设引发的外中断都属于可屏蔽中断。可屏蔽中断也是我们的重点所在。

键盘的中断处理过程

(1)键盘输入
      键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描。
      按下一个键时,开关接通,该芯片产生一个扫描码,称其为通码。通码说明了按下的键在键盘上的位置。通码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60h。
      松开按下的键时,也产生一个扫描码,称其为断码。断码说明了松开的键的位置,也被送入到60h端口中。
      扫描码的长度为一个字节,通码的第 7 位为0,断码的第 7 位为1。 断码 = 通码 + 80h。比如 g 键的通码为 22h,断码为 a2h。
(2)引发 9 号中断
      当键盘输入到达 60h 端口时,相关芯片会对CPU发出中断类型码为 9 的可屏蔽中断。如果此时 IF=1,则响应中断,引发中断过程,执行相应的中断处理程序。
(3)执行 int 9 中断例程
       读出60h端口中的扫描码。
       如果为字符键的扫描码,将它和它对应的字符码(ASCII码)送入内存中的BIOS键盘缓冲区;如果是控制键(Ctrl、Caps)的扫描码,将它转变为状态字节写入内存中存储状态字节的单元。
       对键盘系统进行相关的控制,如,对相关芯片发出应答信息。

      补充:BIOS键盘缓冲区是系统启动后,BIOS用于存放 int 9 中断例程所接收的键盘输入的内存区。该内存区可以存储15个键盘输入,int 9 中断例程除了接收扫描码外,还需要产生和扫描码对应的字符码,故:在缓冲区中,一个键盘输入用一个字单元存放,高位字节存放扫描码,低位字节存放字符码。
      内存单元 0040:17 存储键盘状态字节,用来记录控制键和切换键的状态。比如说:按下 Ctrl 键,对应的位置1,松开置0。按下 Insert 键,对应的位置1表示处于删除态,否则置0。

编写 int 9 中断例程

       编程:在屏幕中间依次显示‘a’-’z’,并可以让人看清。在显示的过程中,按下Esc键后,改变显示的颜色。(Esc键的通码是1)
       思路:在键盘输入到达60h端口后,就会引发9号中断,CPU转而去执行 int 9 中断例程。我们编写 int 9 中断例程的功能如下:
      (1)从60h读取键盘输入,这是为了对特定输入稍后进行处理。
      (2)调用BIOS的 int 9 中断例程,处理其他细节。
调用原 int 9 的中断过程需要有4步:
      a,取中断类型码9;
      b,pushf;
      c,置 IF、TF为0;
      d,设置新的CS:IP。
其中a步骤可以忽略,并且c步骤也是可以忽略的,因为在CPU进入到我们编写的新 int 9 中断例程后,IF、TF已经置为0了,而执行指令 in al,60h 后不会改变IF、TF的值,所以c被省略。
      补充:c步骤代码如下
            pushf
            pop bx
            and bh,11111100b       IF、TF在标志寄存器的第9位和第8位。
            push bx
            popf
      (3)判断是否为Esc的扫描码,若是,改变显示的颜色,否则直接返回。

		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 例程保存在ds:0处,
			   pop ds:[0]			;因为需要对常规的键盘输入进行处理。
			   push es:[9*4+2]
			   pop ds:[2]

			   
			   cli									;这几句是将我们编写的新的 int 9 例程作为默认的
			   mov word ptr es:[9*4],offset int9	;键盘输入的中断处理程序。应该注意到:
			   mov es:[9*4+2],cs					;在此并没有将新的例程安装到0:200处,
			   sti									;因为新的 int 9例程只在本程序执行期间
			  										;才会被使用,程序结束后不再被调用,所以无需在内存中保存。
			  										;并且在执行过程中需要屏蔽CPU对键盘中断的响应。
			   
			   
			   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								;在程序结束后,临时的 int 9 例程不再被使用,需要将
			   mov es,ax							;中断向量表原 int 9 的入口地址恢复。
			   cli									;但是在对中断例程的入口地址更改时,应该禁止对键盘中断的响应,
			   push ds:[0]							;否则CPU会跳到一个错误的中断处理程序入口地址。
			   pop es:[9*4]
			   push ds:[2]
			   pop es:[9*4+2]
			   sti
			   
			   mov ax,4c00h
			   int 21h


		delay: push ax				;cpu执行指令的循环次数为10000000h次,以此作为延迟显示的时间间隔
			   push dx
			   mov dx,1000h
			   mov ax,0
		sl:    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				;读取键盘输入
			   
			   pushf					;模仿调用int 9中断例程来处理其他硬件细节
			   call dword ptr ds:[0]

			   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

实验15

      安装一个新的 int 9 中断例程,功能:在DOS下,按下“A”键后,除非不再松开,如果松开,就显示满屏幕的“A”,其他的键照常处理。(断码=通码+80h。“A”的通码为1Eh)

			assume cs:code
		
			stack segment 
				db 128 dup(0)
			stack ends

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

					mov ax,cs			;将ds:si处的新int 9例程安装到内存0:204h处。
					mov ds,ax
					mov si,offset int9
					mov ax,0
					mov es,ax
					mov di,204h
					mov cx,offset int9end-offset int9
					cld
					rep movsb

					push es:[9*4]		;将原来的int 9入口地址存储在内存0:200h-0:203h处。
					pop es:[200h]
					push es:[9*4+2]
					pop es:[202h]

					cli
					mov word ptr es:[9*4],204h		;将新的int 9例程的入口地址放在中断向量表中。
					mov word ptr es:[9*4+2],0
					sti
					
					mov ax,4c00h
					int 21h
					
			  int9: push ax
			  		push es
			  		push di
			  		push cx
			  		
			  		in al,60h
			 		pushf
			 		call dword ptr cs:[200h]
			 		
			 		cmp al,1eh+80h		;判断是否为“A”的断码
			 		jne int9ret
			 		
			 		mov ax,0b800h
			 		mov es,ax
			 		mov di,0
			 		mov cx,2000
			 	s:  mov byte ptr es:[di],'A'
			 		add di,2
			 		loop s
			 		
		   int9ret: pop cx
		   			pop di
		   			pop es
		   			pop ax
		   			iret
		   int9end: nop
		   
		   code ends
		   end start

おすすめ

転載: blog.csdn.net/Little_ant_/article/details/108646444