【笔记】8086汇编-外中断

前言:本篇仅仅是笔记,整理于2017/9/4,结构可能会略有混乱,见谅。

-引入:CPU除了能够运算外,还能够对外部设备进行控制,也就是说,它除了有运算能力外,还要有I/O能力。【两个问题:CPU如何知道外部输入发生、CPU从哪里得到外设的输入】要及时处理随时可能发生的外部输入,就需要我们的外中断。这一章我们以键盘输入为例。

-【15.1  接口芯片的端口】

–外设的输入不是直接传给计算机的,而是先送入相关的接口芯片的端口中;对称的,CPU向外输出也是要经过端口,再由端口给芯片,再给外设输出【CPU控制外设也是同理】

–综上所述,CPU通过端口和外部设备进行联系。

-【15.2  外中断信息】

–当有外部输入时,相关芯片向CPU发送中断信息,CPU在执行完当前指令后,可以检索中断信息,引发中断。

–PC中,外中断源一共有以下两类:

—1)可屏蔽中断

—-这种中断并不是一定会得到CPU的响应,CPU检测到这种信息时,如果IF=1,CPU会执行完当前指令后中断,而IF=0时,可不响应中断。【IF(interrupt flag),指示是否响应中断】【顺便补充前面的遗漏,TF(tarp flag)陷阱标志,指示是否单步调试】

—-这种外中断与内中断的流程相似,过程:总线传入中断类型码→标志寄存器入栈,IF=0,TF=0→CS、IP入栈→CS与IP正确变更【通过中断向量表,访问中断向量表仍然是将中断类型码×4得到向量表的IP位置,再加2得到中断向量表的CS】

—-现在我们可以解释为什么会将IF设置为0了,因为进入中断处理程序后,当然需要禁止其它的可屏蔽中断,不过,8086CPU也提供了设置IF的指令【所以让中断例程响应可屏蔽中断也是可以的】:

—–sti,设置IF为1

—–cli,设置IF为0

—2)不可屏蔽中断

—-不可屏蔽中断会在CPU执行完当前指令后立即响应,引发中断。

—-对于8086CPU,不可屏蔽中断类型码固定为2,所以中断过程中不需要取得类型码,则其中断过程为:标志寄存器入栈,IF=0,TF=0→CS、IP入栈→(IP)=8,(CS)=(0AH)。

–几乎所有外设引发的外中断,都是可屏蔽中断。不可屏蔽中断,是在系统中有必须处理的紧急情况发生时用来通知CPU的中断信息,这里主要讨论可屏蔽中断。

-【15.3  PC机键盘的处理过程】

–①键盘输入:键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描。按下一个键,开关接通,芯片产生扫描码(用来描述位置),扫描码会被送入主板上相关接口芯片的寄存器中,这个寄存器端口为60h。松开与按下,都会产生一个扫描码【扫描码是1个字节的数据】,说明了松开或按下的键在键盘上的位置。

–一般我们将按下一个键的扫描码称为通码,松开一个键产生的扫描码称为断码。通码与断码区别在于第7位,通码的第7位是0,断码的第7位是1,也就是说:断码=通码+80h。

–需要注意:shift的扫描码分键盘左右shift,而alt则不分左右。

–②引发9号中断:键盘输入到达60h端口时,相关芯片就会向CPU发出中断类型码为9的可屏蔽中断,如果这时IF=1,CPU检测到中断后就会响应。

–③执行int 9中断例程:BIOS提供了int 9 中断例程,用来进行基本的键盘输入处理,主要工作如下:

—1)读出60h端口的扫描码

—2)处理方式:

—-字符键扫描码:将扫描码与对应字符码(ASCII)送入BIOS键盘缓冲区(这个缓冲区可以存储15个键盘输入);

—-控制键(如ctrl)和切换键(如CapsLock):将其转变为状态字节(用二进制位记录控制键和切换键状态的字节),写入内存中存储状态字节的单元(0040:17)

—3)相关控制:BIOS键盘缓冲区是系统启动后,BIOS用于存放int 9中断例程所接受的键盘输入的内存区,如上述,可以存放15个键盘输入,每个键盘输入用一个字单元来存储(扫描码和ASCII码),高位存放扫描码,低位存放字符码。键盘状态字节各位记录如下:

—-0:右shif。1:左shift。2:Ctrl。3:Alt。【1表示按下】

—-4:ScrollLock。5:Numlock。6:CapsLock。7:Insert。【1表示开启】

-【15.4  编写int9中断例程】

–梳理一下处理过程:产生扫描码→进入60h端口→引发9号中断→CPU执行int 9中断例程处理键盘输入。只有最后一步,是我们能够在不改变硬件的情况下进行改变。

–我们想要让我们的中断例程能够读取ESC,功能如下:

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

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

—3)判断是否为ESC的扫描码,如果是,改变显示颜色后返回;如果不是则直接返回。

–细节问题:我们想要在新的程序中调用原来的int 9h中断例程,所以我们需要将其原始地址提前保存。并且我们需要模拟中断调用:

—标志寄存器入栈→IF、TF=0、call dword ptr ds:[0]

—关于这里同时修改TF、IF,我们可以通过pushf后将其读入ax,在对ah使用and指令【11111100b】,再入栈,再popf

—不过,由于我们是在我们自己的中断例程调用的原中断例程,所以IF、TF已经被设置为0,所以我们可以略过设置IF、TF这个步骤,直接进行pushf,再call就可以了。

—我们还应当注意一个问题,那就是:在设置int 9中断例程的段地址和偏移地址的指令间也可能发生中断,这时CPU会转去了错误地址,所以,我们应当使用sti与cli指令,准去来说,在设置前使用cli,设置后使用sti,这样可以屏蔽掉可屏蔽中断。

-【15.5  安装新的int 9中断例程】

–和以往的安装中断例程类似,都要安装在0:200处,不过这里,我们建议先将原来的int 9向量存放在0:200和0:202,从204开始存放代码。

—关于备份旧的向量,我们可以直接使用栈来操作,这样是最便捷的:

—-push es:[9*4]

—-pop es:[200h]

—-push es:[9*4+2]

—-pop es:[202h]

–关于新地址设置:

—由于与外中断有关,所以我们这样写【原因见15.4最后一点】:

—-cli

—-mov word ptr es:[9*4],204h

—-mov word ptr es:[9*4+2],0

—-sti

-【总结】

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