CPU除了有运算能力外,还要有I/O能力
要及时处理外设的输入,显然需要解决两个问题:
外设的输入随时可能发生,
CPU如何得知?CPU从何处得到外设的输入?
接口芯片和端口
外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中
CPU向外设的输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设
CPU还可以向外设输出控制命令,这些控制命令先送到相关芯片的端口中,然后再由相关的芯片根据命令对外设实施控制
可见,CPU通过端口和外部设备进行联系
外中断信息
除了内中断外,还有一种中断信息,来自于CPU外部,当CPU外部有需要处理的事情发生时,相关芯片会向CPU发出相应的中断信息。CPU在执行完当前指令后,可以检测到发送过来的中断信息,引发中断过程,处理外设的中断
在PC系统中,外中断源分为:
可屏蔽中断
不可屏蔽中断
可屏蔽中断
CPU可以不响应的外中断
CPU是否响应可屏蔽中断,取决于标志寄存器IF位的设置
当CPU检测到可屏蔽中断信息时,若IF=1,则CPU在执行完当前指令后响应中断,引发中断过程;若IF=0,则不响应可屏蔽中断
可屏蔽中断的中断过程:
通过数据总线将中断类型码
n送入CPU标志寄存器入栈,
IF=0,TF=0CS、IP入栈(IP)=(n*4),(CS)=(n*4+2)
中断过程将IF置为0的原因是:在进入中断处理程序后,禁止其他的可屏蔽中断
若在中断处理程序中需要处理可屏蔽中断,可用指令将IF置1
8086CPU提供的设置IF的指令为:
1 | sti ;IF置为1 |
不可屏蔽中断
不可屏蔽中断是CPU必须响应的外中断
当CPU检测到不可屏蔽中断信息时,则在执行完当前指令后,立即响应,引发中断过程
对于8086CPU,不可屏蔽中断的中断类型码固定为2
不可屏蔽中断的中断过程:
标志寄存器入栈,
IF=0,TF=0CS、IP入栈(IP)=(8),(CS)=(0AH)
几乎所有由外设引发的外中断,都是可屏蔽中断
当外设有需要处理的事件发生时,相关芯片向CPU发出可屏蔽中断信息
不可屏蔽中断是在系统中有必须处理的紧急情况发生时用来通知CPU的中断信息
PC机键盘的处理过程
键盘输入
键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描
按下一个键时,开关接通,该芯片就产生一个扫描码,扫描码说明了按下的键在键盘上的位置。扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60h
松开按下的键时,也产生一个扫描码,扫描码说明了松开的键在键盘上的位置。松开按键时产生的扫描码也被送入60h端口中
按下一个键时产生的扫描码称为通码,松开一个键产生的扫描码称为断码
扫描码长度为一个字节,通码的第7位为0,断码的第7位为1,即:断码=通码+80h
键盘中部分键的扫描码通码:


引发9号中断
键盘的输入到达60h端口时,相关芯片就会向CPU发出中断类型码为9的可屏蔽中断信息
CPU检测到该中断信息后,若IF=1,则响应中断,引发中断过程,转去执行int 9中断例程
执行int 9中断例程
BIOS提供了int 9中断例程,用于进行基本的键盘输入处理
int 9中断例程主要工作:
读出
60h端口中的扫描码若是字符键的扫描码,将该扫描码和所对应的字符码(
ASCII码)送入内存中的BIOS键盘缓冲区若是控制键和切换键的扫描码,则将其转变为状态字节(用二进制位记录控制键和切换键状态的字节)写入内存中存储状态字节的单元
对键盘系统进行相关的控制,例:向相关芯片发出应答信息
BIOS键盘缓冲区是系统启动后,BIOS用于存放int 9中断例程所接收的键盘输入的内存区。该内存区可以存储15个键盘输入。
在BIOS键盘缓冲区中,一个键盘输入用一个字单元存放,高位字节存放扫描码,低位字节存放字符码
0040:17单元存储键盘状态字节,该字节记录了控制键和切换键的状态
键盘状态字节各位记录的信息为:
| 位 | 表示 | 信息 |
|---|---|---|
| 0 | 右shift状态 | 置1表示按下 |
| 1 | 左shift状态 | 置1表示按下 |
| 2 | Ctrl状态 | 置1表示按下 |
| 3 | Alt状态 | 置1表示按下 |
| 4 | ScrollLock状态 | 置1表示Scroll指示灯亮 |
| 5 | NumLock状态 | 置1表示小键盘输入的是数字 |
| 6 | CapsLock状态 | 置1表示输入大写字母 |
| 7 | Insert状态 | 置1表示处于删除态 |
编写int 9中断例程
键盘输入的处理过程:
键盘产生扫描码
扫描码送入
60h端口引发9号中断
CPU执行int 9中断例程处理键盘输入
第1-3步由硬件系统完成,可控的部分仅为int 9中断处理程序
程序需调用原int 9中断例程去处理硬件细节
编程:在屏幕中间依次显示”a”–”z”,在显示过程中,按下Esc键,改变显示颜色
依次显示”a”–”z”
1 | assume cs:code |
程序执行,无法看清屏幕上的显示。因CPU执行过快,一个字母刚显示,CPU执行几条指令后,就会变为另一个字母
程序执行,设置显示延后
当显示一个字母后,让CPU执行一段时间的空循环
CPU速度较快,所以空循环的次数一定要大,需要使用16位寄存器来存放32位的循环次数
1 | mov dx,10h |
以上程序,循环100000h次
实现按Esc键后,改变显示颜色
键盘输入到达60h端口后,引发9号中断,CPU转去执行int 9中断例程
重新编写int 9中断例程,功能:
从
60h端口中读出键盘的输入调用
BIOS的int 9中断例程,处理其他硬件细节判断是否为
Esc的扫描码,若是,改变颜色后返回;若不是,直接返回
从端口60h读出键盘的输入
1 | in al,60h ;in指令只可使用al或ax接收数据 |
调用BIOS的int 9中断例程
重新写的中断处理程序要成为新的int 9中断例程,主程序需将中断向量表中的int 9中断例程的入口地址改为新中断处理程序的入口地址。则在新中断例程调用原int 9中断例程时,中断向量表中的int 9中断例程并非原中断例程,所以不可使用int指令直接调用
必须在将中断向量表中的中断例程入口地址更改之前,将原入口地址保存
不能使用int 9指令调用原中断例程,使用另外的指令模拟int指令,实现对原中断例程的调用
int指令执行时,CPU的工作:
取中断类型码
n标志寄存器入栈
IF=0,TF=0CS、IP入栈(IP)=(n*4),(CS)=(n*4+2)
取中断类型码是为了定位中断例程的入口地址,此时我们保存了原中断例程的入口地址,所以不需要第一步
使用pushf替代第二步
使用以下程序替代第三步
1 | pushf |
使用call dword ptr xxx替代第四步,第五步
若是Esc的扫描码,改变显示的颜色后返回
显示的位置是屏幕的中间,即第12行40列,所以字符的ASCII码送入段地址b800h,偏移地址160*12+40*2处。而段地址b800h,偏移地址160*12+40*2+1处为字符的属性,改变此处的数据即可改变字符的颜色
在此程序返回前,将中断向量表中int 9中断例程的入口地址恢复为原来的地址
完整程序如下:
1 | assume cs:code |
关于键盘的程序,因直接访问真实的硬件,需在DOS实模式下运行
安装新的int 9中断例程
安装一个新的int 9中断例程,使得原int 9中断例程功能得到扩展
功能:在DOS下,按F1键改变当前屏幕的显示颜色,其他键无改变
改变屏幕的显示颜色
改变从b8000h开始的4000个字节中的所有奇地址单元中的内容,当前屏幕显示颜色即发生改变
1 | mov ax,0b800h |
其他键无改变
可以调用原int 9中断处理程序,用于处理其他的键盘输入
原int 9中断例程入口地址的保存
因新编写的int 9中断例程中需使用到原int 9中断例程,所以,要保存原int 9中断例程的入口地址
不可保存在安装程序中,因安装程序返回后地址将丢失。将地址保存在0:200单元中
新int 9中断例程的安装
将新的int 9中断例程安装在0:204处
完整程序如下:
1 | assume cs:code |
CPU对外设输入的通常处理方法:
外设的输入送入端口
向
CPU发出外中断(可屏蔽中断)信息CPU检测到可屏蔽中断信息,若IF=1,CPU在执行完当前指令后响应中断,执行相应的中断例程可在中断例程中实现对外设输入的处理
指令系统总结
数据传送指令
实现寄存器和内存、寄存器和寄存器之间的单个数据传送
例:mov、push、pop、pushf、popf、xchg
算术运算指令
实现寄存器和内存中的数据的算数运算
执行的结果影响标志寄存器的SF、ZF、OF、CF、PF、AF位
例:add、sub、abc、sbb、inc、dec、cmp、imul、idiv、aaa
逻辑指令
例:and、or、not、xor、test、shl、shr、sal、sar、rol、ror、rcl、rcr
除not指令外,其他指令所执行的结果都影响标志寄存器的相关标志位
转移指令
可以修改IP,或同时修改CS和IP的指令统称为转移指令
无条件转移指令,例:
jmp条件转移指令,例:
jcxz、je、jb、ja、jnb、jna等循环指令,例:
loop过程,例:
call、ret、retf中断,例:
int、iret
处理机控制指令
对标志寄存器或其他处理机状态进行设置
例:cld、std、cli、sti、nop、clc、cmc、stc、hlt、wait、esc、lock
串处理指令
对内存中的批量数据进行处理
例:movsb、movsw、cmps、scas、lods、stos等
要方便的进行批量数据的处理,需和rep、repe、repne等前缀指令配合使用