外中断

CPU在计算机系统中,除了能够执行指令,进行运算以外,还应该能够对外部设备进行控制,接收它们的输入,向它们进行输出。也就是说,CPU除了有运算能力外,还要有I/O(Input/Output,输入/输出)能力。比如,按下键盘上的一个键,CPU最终要能够处理这个键。在使用文本编辑器时,按下a键后,可以看到屏幕上出现“a"'是CPU将从键盘上输入的键所对应的字符送到显示器上的。

接口芯片和端口

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

外中断信息

当CPU外部有需要处理的事情发生的时候,比如说,外设的输入到达,相关芯片将向CPU发出相应的中断信息。CPU在执行完当前指令后,可以检测到发送过来的中断信息,引发中断过程,处理外设的输入。

在PC 系统中,外中断源一共有以下两类:

1. 可屏蔽中断

可屏蔽中断是CPU可以不响应的外中断。CPU是否响应可屏蔽中断,要看标志寄存器的IF位的设置。当CPU检测到可屏蔽中断信息时,如果IF=l,,则CPU在执行完当前指令后响应中断,引发中断过程;如果IF=0,则不响应可屏蔽中断。
汇编语言-15外中断_汇编技术

2. 不可屏蔽中断

不可屏蔽中断是CPU必须响应的外中断。当CPU检测到不可屏蔽中断信息时,则在执行完当前指令后,立即响应,引发中断过程。
不可屏蔽中断的中断类型码固定为2,所以中断过程中,不需要取中断类型码。则不可屏蔽中断的中断过程为:
(1) 标志寄存器入栈,IF=O,TF=O:
(2) cs、IP 入栈;
(3) (IP)=(8),(CS)=(OAH) 。
几乎所有由外设引发的外中断,都是可屏蔽中断。当外设有需要处理的事件(比如说键盘输入)发生时,相关芯片向CPU发出可屏蔽中断信息。不可屏蔽中断是在系统中有必须处理的紧急情况发生时用来通知CPU的中断信息。

PC机键盘的处理过程

键盘输入

键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描。
按下一个键时,开关接通,该芯片就产生一个扫描码,扫描码说明了按下的键在键盘上的位置。扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60h。
松开按下的键时,也产生一个扫描码,扫描码说明了松开的键在键盘上的位置。松开按键时产生的扫描码也被送入60h端口中。
一般将按下一个键时产生的扫描码称为通码,松开一个键产生的扫描码称为断码。扫
描码长度为一个字节,通码的第7位为0,断码的第7位为1,即:
断码=通码+80h
比如,g键的通码为22h,断码为a2h。
汇编语言-15外中断_汇编语言_02
汇编语言-15外中断_汇编技术_03

引发9号中断

键盘的输入到达60h端口时,相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息。CPU检测到该中断信息后,如果IF=1,则响应中断,引发中断过程,转去执行int9中断例程。

执行int 9中断例程

汇编语言-15外中断_汇编技术_04

编写int 9 中断例程

1.键盘产生扫描码;2.扫描码送入60h端口;3.引发9号中断;4.CPU执行int 9中断例程处理键盘输入。
第1 、2 、3步都是由硬件系统完成的。能够改变的只有int 9中断处理程序。

显示a~z

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,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,4c00h
		int 21h

	delay:push ax
		push dx
		mov dx,l000h  ;循环10000000h次,读者可以根据自己机器的速度调整循环次数
		mov ax,0
	s1: sub ax,1
		sbb dx,0
		cmp ax,0
		jne s1
		cmp dx,O
		jne s1
		pop dx
		pop ax
		ret
code ends
end start

按下Esc键后改变颜色

(1) 从60h端口读出键盘的输入;
(2) 调用BIOS的int 9中断例程,处理其他硬件细节;
(3) 判断是否为Esc的扫描码,如果是,改变显示的颜色后返回;如果不是则直接返回。

1.从端口60h 读出键盘的输入

in al,60h

2.调用BIOS 的int 9中断例程

int 9

int 指令在执行的时候,CPU 进行下面的工作。
(1) 取中断类型码n;
(2) 标志寄存器入栈;
(3) IF=0, TF=0;
(4) cs 、IP入栈;
(5) (IP)=((ds)16+0), (CS)=((ds)16+2)。

第(4) 、(5)步和call dword ptr ds:[O] 的功能一样

call dword ptr ds:[0] 的功能:
(1) cs 、JP 入栈;
(2) (IP)=((ds)16+0), (CS)=((ds)16+2) 。

int 过程的模拟过程变为:

(1) 标志寄存器入栈;
(2) IF=0, TF=0:
(3) call dword ptr ds:[0] 。

对于(1) ,可用pushf 实现;

对于(2) ,可用下面的指令实现:

pushf
pop ax
and ah,11111100b  ;IF 和TF 为标志寄存器的第9位和第8位
push ax
popf

则模拟int指令的调用功能,调用入口地址在ds:0 、ds:2中的中断例程的程序为:

pushf    ;标志寄存器入栈

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

call dword ptr ds:[0]   ;CS、IP入栈;(IP)= ((ds)*16+0), (CS)= ((ds)*16+2)

显示的位置是屏幕的中间,即第12行40列,显存中的偏移地址为:160 * 12+40 * 2 。所以字符的ASCII 码要送入段地址b800h, 偏移地址16012+402 处。段地址b800h,偏移地址160 * 12+40 * 2+1 处是字符的属性,只要改变此处的数据就可以改变在段地址b800h, 偏移地址160* 12+40* 2处显示的字符的颜色了。

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]     ;将原来的int 9中断例程的入口地址保存在ds:0、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,O
		mov es,ax
	
		push ds:[0]
		pop es:[9*4]
		push ds:[2]
		pop es:[9*4+2]    ;将中断向量表中int 9中断例程的入口恢复为原来的地址
	
		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

;-----------以下为新的int 9 中断例程-----------------
    int9:push ax
        push bx
        push es

        in al,60h

        pushf
        pushf
        pop bx
        and bh, 11111100b
        push bx
        popf
        call dword ptr ds:[0]   ;对int 指令进行模拟,调用原来的int 9 中断例程

        cmp al,1
        jne int9ret

        mov ax,0bB00h
        mov es,ax
        inc byte ptr es:[160*12+40*2+1]   ;将属性值加1, 改变颜色
    int9ret:pop es
        pop bx
        pop ax
        iret

code ends
end start

关于键盘的程序,因要直接访问真实的硬件,则必须在DOS 实模式下运行。在Windows 2000 的DOS 方式下运行,会出现一些和硬件工作原理不符合的现象。

安装新的int 9中断例程

(1) 改变屏幕的显示颜色

改变从B8000H 开始的4000个字节中的所有奇地址单元中的内容,当前屏幕的显示颜色即发生改变。

mov ax,0b800h
mov es,ax
mov bx,l
mov cx,2000
s:inc byte ptr es:[bx]
add bx,2
loops

(2) 其他键照常处理
可以调用原int 9中断处理程序,来处理其他的键盘输入。

(3) 原int 9中断例程入口地址的保存

因为在编写的新int 9中断例程中要调用原int 9中断例程,所以,要保存原int 9中断例程的入口地址。将地址保存在0:200单元处。

(4) 新int 9 中断例程的安装

将新的int 9 中断例程安装在0:204 处。

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,O
        mov es,ax

        mov si,offset int9    ;设置ds:si指向源地址
        mov di,204h    ;设置es:di 指向目的地址
        mov cx,offset int9end-offset int9    ;设置ex 为传输长度
        cld    ;设置传输方向为正
        rep movsb

        push es:[9*4]
        pop es:[200h]
        push es:[9*4+2]
        pop es:[202h]

        cli
        mov word ptr es:[9*4],204h
        mov word ptr es:[9*4+2],0
        sti

        mov ax,4c00h
        int 21h

	int9:push ax
        push bx
        push cx
        push es
        
        in al,60h
        
        pushf
        call dword ptr cs:[200h]    ;当此中断例程执行时(CS)=O
        
        cmp al,3bh     ;Fl的扫描码为3bh
        jne int9ret
        
        mov ax,Ob800h
        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 bx
        pop ax
        iret
        
int9end:nop

code ends
end start

总结

CPU 对外设输入的通常处理方法:

(1) 外设的输入送入端口;
(2) 向CPU 发出外中断(可屏蔽中断)信息;
(3) CPU检测到可屏蔽中断信息,如果IF=1, CPU在执行完当前指令后响应中断,执行相应的中断例程;
(4) 可在中断例程中实现对外设输入的处理。

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