《汇编语言》第三版有关中断处理的学习笔记。
相关概念
什么是中断?
一种来自CPU内部或者外部的中断信息,被CPU检测到,CPU在执行完当前正在执行的指令后,可以立即对所接收到的信息进行处理。
Wiki上的解释更为简单点:
In digital computers, an interrupt is an input signal to the processor indicating an event that needs immediate attention.
中断可分为哪些类型?
按照是否发生在CPU内部,分为:
内中断:发生在CPU内部的中断, 比如溢出。
外中断:发生在CPU外部的中断,比如外部设备输入
中断处理的基本机制是什么?
一般分四步:
- 获取中断类型码 N
- 标志寄存器入栈,TF,IF 设置为0
- CS,IP入栈
- (IP)= (N * 4), (CS) = (N * 4 + 2)
其中,外中断与内中断在第一步,即获取中断类型码N稍有不同, 因为外中断的中断信息来自CPU外部,中断类型码通过数据总线送入CPU的, 而内中断的中断类型码是直接在CPU内部产生的。
中断后面的三步可转换成如下汇编语言:
; 标志寄存器入栈 pushf ; 设置 TF,IF 为0,TF,IF为标志寄存器的8,9位 pushf pop bx and bh, 11111100b push bx popf ; CS,IP入栈 push CS push IP
这里解释下为什么要设置 TF=0, IF=0。
TF 与单步中断有关
单步中断的类型码为1, CPU检测TF,如果 TF =1,则产生单步中断,CPU会去执行1号中断处理程序,故这里将TF设置为0。
IF与外中断相关
CPU可以不响应的外中断称为 可屏蔽中断。CPU通过IF的值决定是否响应可屏蔽中断。
当 IF=1,CPU执行完当前指令后,响应中断,引发中断过程。IF=0,CPU不响应可屏蔽中断。
所以这里设置IF=0,这样,在进入中断例程后,便禁止了其他的可屏蔽中断。
如何根据中断类型码找到对应的中断例程的入口地址?
通过中断向量表 IDT Interrupt description table。
对8086机,中断向量表指定存放在内存地址0处,共有256种中断类型,一个表项存放一个中断例程的入口地址,即CS:IP,故整个中断向量表占用 4 byte * 256 = 1024 byte,即内存地址从0000:0000 到 0000:03FF的1024个单元中存放着中断向量表。
中断例程有哪些?
中断例程分两种:
程序员自己编写的
系统自带的:可由 BIOS 或 DOS提供。
BIOS:基本输入输出系统,存放在ROM中。
BIOS 中主要包括的中断:
- 外部中断和内部中断的中断例程
- 用于对硬件设备进行 I/O操作的中断例程 「比如在屏幕进行打印,int 10h」
- 其他和硬件系统相关的中断例程
操作系统DOS 也提供了中断处理,注意,和硬件设备相关的DOS 中断例程中,一般都调用了BIOS中的中断例程。
BIOS, DOS 提供的中断例程,都是使用ah 来传递子程序的编号。
比如(ah)=9, 表示调用第 N 号中断例程的9号子程序。
8086支持的中断例程列表:bios and dos interrupts
程序员如何自己编写中断处理程序?
三步走:
编写中断处理程序
将中断处理程序复制到指定的内存地址中
设置中断向量表
这里,将中断处理程序复制到内存中哪个地方合适?
内存地址从0000:0000 到 0000:03FF的1024个单元中,存放着256种中断类型的入口地址,但实际上,系统要处理的中断事件远没有达到256个,一般情况下,从0000:0200 到 0000:02FF 的256个字节所对应的中断向量表项都是空的,可以安放在此处。
有哪些常见的中断例程?
int 10h:
BIOS 提供, (ah)=2h, 表示调用第 10h 号中断例程的2号子程序,置光标, (ah)=9h, 表示调用第 10h 号中断例程的9号子程序,在光标位置显示字符。
int 21h:
DOS 提供, (ah)=4ch, 表示调用第 21h 号中断例程的4c号子程序,即退出程序, (ah)=9h, 表示调用第 21h 号中断例程的9号子程序,在光标位置显示字符串。
int 9h:
BIOS 提供。从60h端口读出输入的扫描码,并将其转化为相应的ASCII码或状态信息,存储在内存的指定空间(键盘缓冲区/状态字节)中。
Int 16h
BIOS提供,该中断例程包含的0号子程序,可以从键盘缓冲区中读取一个键盘的输入。
端口读写
外中断的处理,涉及到端口读写,来了解下。
CPU 可以直接读写3个地方的数据:
- CPU内部的寄存器
- 内存单元
- 端口
CPU 对端口的读写只有两个指令: in (读取), out(写入).
读写端口,只能通过 ax / al, 即读入的信息和写入的信息都通过 ax / al 传递。
端口地址范围:0~65535
0~255的端口读写:
in al, 20h out 20h, al
256~65535的端口读写:
mov dx, 3ffh ;需要一个16位寄存器来存放端口地址 in al, dx out dx, al
有关外中断
主要为处理外部设备的输入输出,比如键盘的输入。
外设与CPU交互通过端口实现:
外设的输入先送入相关的接口芯片的端口,CPU通过访问端口来读取外设的输入。
同样,CPU向外设的输出也不是直接送入外设,而是先送入端口,再由相关芯片送入外设,即端口相当于一个中间站。CPU通过端口与外设联系。
外中断分为两类:
可屏蔽中断: CPU 可以不响应的外中断
通过 标志寄存器中的 IF位来判断是否屏蔽。
8086CPU提供了设置IF的指令:
sti ,设置 IF=1
cli,设置 IF=0
几乎所有由外设引发的外中断,都是可屏蔽中断。
不可屏蔽中断:CPU必须响应的外中断
对于8086CPU, 不可屏蔽中断的中断类型码固定为2。
不可屏蔽中断是系统中,有必须处理的紧急情况发生时,用来通知CPU的中断信息。
以键盘输入引发的中断为例:
松开或者按下键盘时,会产生一个扫描码,该扫描码会被送入60h 端口,引发9号中断,CPU检测到该中断后,如果IF=1,则响应中断,引发中断过程,去执行int 9h 中断例程。
即整个流程为:
- 键盘产生扫描码
- 扫描码送至60h端口
- 引发9号中断
- CPU执行BIOS提供的 int 9h 中断例程