汇编语言-15外中断

## 外中断

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,则不响应可屏蔽中断。
![](https://img2020.cnblogs.com/blog/1208477/202007/1208477-20200714082006601-214202247.png)

### 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。
![](https://img2020.cnblogs.com/blog/1208477/202007/1208477-20200714082954409-1017429178.png)
![](https://img2020.cnblogs.com/blog/1208477/202007/1208477-20200714083007355-1071023312.png)

### 引发9号中断

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

### 执行int 9中断例程

![](https://img2020.cnblogs.com/blog/1208477/202007/1208477-20200714083139682-1287141444.png)

### 编写int 9 中断例程

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

显示a~z

“`assembly
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 读出键盘的输入

“`assembly
in al,60h
“`

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

“`assembly
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) ,可用下面的指令实现:

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

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

“`assembly
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, 偏移地址160*12+40*2 处。段地址b800h,偏移地址160 * 12+40 * 2+1 处是字符的属性,只要改变此处的数据就可以改变在段地址b800h, 偏移地址160* 12+40* 2处显示的字符的颜色了。

“`assembly
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个字节中的所有奇地址单元中的内容,当前屏幕的显示颜色即发生改变。

“`assembly
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 处。

“`assembly
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 的基础。

点赞

Leave a Reply

Your email address will not be published. Required fields are marked *