X86汇编语言从实模式到保护模式08:中断和动态时钟显示

 

目录

1. 外部硬件中断

1.1 概述

1.2 外部硬件中断分类

1.2.1 概述

1.2.2 不可屏蔽中断

1.2.3 可屏蔽中断

1.3 中断控制器

1.3.1 引入中断控制器的原因

1.3.2 8259A中断控制器

1.4 如何屏蔽中断

1.4.1 中断控制器级屏蔽

1.4.2 处理器级屏蔽

1.5 实模式中断向量表

1.6 中断处理过程

1.6.1 保护断点现场

1.6.2 执行中断处理程序

1.6.3 返回断点继续执行

2. RTC中断布署实例

2.1 RTC概述

2.2 CMOS RAM

2.2.1 CMOS RAM中的信息

2.2.2 如何访问CMOS RAM

2.3 RTC控制寄存器说明

2.3.1 寄存器A

2.3.2 寄存器B

2.3.3 寄存器C(只读)

2.3.4 寄存器D

2.4 安装0x70号中断过程

2.4.1 设置段寄存器

2.4.2 修改0x70中断向量

2.4.3 RTC & 中断控制器设置

2.4.4 使处理器进入低功耗状态

2.5 RTC中断处理程序分析

2.5.1 保护中断现场

2.5.2 读取RTC时间

2.5.3 读清中断标志

2.5.4 显示RTC时间

2.5.5 向8259发送EOI

2.5.6 中断返回

2.6 上机验证

3. 内部中断与软中断

3.1 内部中断

3.1.1 内部中断含义

3.1.2 内部中断示例

3.1.3 内部中断特性

3.2 软中断

3.3 BIOS中断

3.3.1 使用中断提供服务的好处

3.3.2 BIOS中断的部署

3.3.3 BIOS中断使用示例


 

1. 外部硬件中断

1.1 概述

① 中断就是打断处理器当前的执行流程,去执行另外一些和当前工作不相干的指令,执行完成后,还可以返回到原来的程序流程继续执行

② 外部硬件中断提出的背景是外设比处理器慢很多,因此当一个程序需要等待外设的输入输出时,处理器可以先切换执行其他程序,当外设就位时,通过中断通知处理器

1.2 外部硬件中断分类

1.2.1 概述

① 外部硬件中断通过两个信号线进入处理器内部,分别为不可屏蔽中断NMI(Non Maskable Interrupt)和可屏蔽中断INTR

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

② 来自外部设备的中断很多,通过NMI和INTR两个信号线,可以区分两种不同性质的中断

③ 每种类型的中断都被统一编号,称作中断类型号、中断向量或者中断号,处理器据此采取适当的处理措施

1.2.2 不可屏蔽中断

① 关系到整个系统的安全性,在任何时刻都必须及时处理。比如内存访问电路发现校验错误,这意味着从内存读取的数据是错误的,后续运行的正确性已无法保障

② 在实模式下,NMI被赋予统一的中断号2,不再进行细分

③ 产生不可屏蔽中断的中断源一般通过与非门连接到NMI引脚,一旦有一个中断源生效,则可以触发处理器的NMI中断

1.2.3 可屏蔽中断

① 可以被忽略或者延迟处理

② 产生可屏蔽中断的中断源数量很多,一般通过中断控制器进行管理

1.3 中断控制器

1.3.1 引入中断控制器的原因

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

① 由于外部中断数量很多,不可能为每个中断源都提供一个到处理器的引脚。因此需要一个代理,来接受外部设备发出的中断信号

② 多个设备同时发出中断的概率也很高,因此该代理的任务还包括对他们进行仲裁,决定让哪一个中断源优先向处理器提出服务请求

说明:处理器每次只能处理一个中断

1.3.2 8259A中断控制器

本教程以8259A作为中断控制器的示例

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

① Intel处理器允许256个中断,中断号范围为0 ~ 255

② 通过级联两片8259A,负责提供其中的15个,但中断号并不固定,而是允许软件根据自己的需要灵活设置中断号,以防止发生冲突

③ 中断控制器芯片有自己的端口号,可以使用in & out指令实现控制,其中就包括对各引脚中断号的设置

④ 对8259A中断号的设置,只能以芯片为单位,即只能设置IR0的中断号,之后依序增加。BIOS设置的默认中断号如下图所示,其中后文要使用的RTC中断号为0x70

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

说明1:由于可以通过软件控制8259A芯片,因此他又被称作可编程中断控制器(Programmable Interrupt Controller,PIC)

说明2:8259A中断优先级

① 默认情况下,中断优先级和引脚相关,主片的IR0引脚优先级最高,IR7最低,从片也是如此。当然,需要注意从片是级联在主片的IR2引脚上的

② 8259A的中断优先级可以软件设置

说明3:关于中断嵌套

① 在计算机内部,中断发生得非常频繁,当一个中断正在处理时,其他中断也会陆续到来。8259A芯片会记住他们,并按一定的策略决定先为谁服务

② 当一个中断事件正在处理时,如果来了一个优先级更高的中断,允许暂时中止当前的中断处理,先为优先级更高的中断服务,这称为中断嵌套

说明4:处理器响应中断的时机

① 当中断发生时,如果从外部硬件到处理器之间的道路是畅通的,处理器在执行完当前指令后,会立即着手为硬件服务

② 处理器会首先响应中断(ACK),告诉8259A芯片准备着手处理该中断。接着,处理器会要求8259A芯片将中断号发送过来

1.4 如何屏蔽中断

下面来讨论如何屏蔽可屏蔽中断~

1.4.1 中断控制器级屏蔽

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

① 在8259A芯片内部,有中断屏蔽寄存器(Interrupt Mask Register,IMR)

② IMR为8位,对应8个中断输入引脚,用于决定从该引脚来的中断信号是否能通过8259送往处理器

③ 在中断屏蔽寄存器中,0表示允许,1表示阻断

1.4.2 处理器级屏蔽

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

① 可屏蔽中断能否被处理,最终决定权在处理器手中

② 当中断寄存器中的中断标志位IF为0时,所有从INTR引脚进入的中断信号均被忽略掉;当中断标志位IF为1时,处理器可以接收和响应中断

说明:屏蔽中断分为中断源级、中断控制器级和处理器级

1.5 实模式中断向量表

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

① 所谓中断处理,归根结底是要处理器执行一段与该中断有关的指令,称为中断处理程序

② 在实模式下,处理器将所有中断处理程序的入口点集中存放在内存中从物理地址0x00000开始,到0x003FF结束,共1KB的空间内,称为中断向量表(Interrupt Vector Table,IVT)

③ 每个中断在中断向量表中占2个字,分别是中断处理程序的偏移地址(低字)和段地址(高字)

④ 由于中断向量按顺序存放,所以通过中断号可以索引到对应的中断向量(将中断号 * 4)

⑤ 系统启动后,实模式下的中断向量表由BIOS设置

说明1:关于实模式中断向量表的位置

① 在8086中,只有实模式,中断向量表的位置固定从物理地址0x00000开始

② 在80386中,引入了idtr寄存器,用于记录中断向量表 & 中断描述符表的起始地址,在实模式下,将其设置为从物理地址0x00000开始,这也实现了与8086的兼容

这也意味着,在实模式下,中断向量表的位置是可以修改的

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

说明2:在bochs中查看实模式中断向量表

由于实模式下的中断向量表是BIOS设置的,因此需要在BIOS运行完成后查看中断向量表区域,我们在运行进入MBR时进行观察

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

注意3点,

① 很多中断的处理方式相同,所以中断处理程序的入口地址是相同的

② xp命令中的h,是以半字显示。由于80386是32位处理器,所以字为4B,半字为2B

③ 操作系统和用户程序可以根据自己的需要,修改中断向量表中的入口地址,使他指向自己的代码

1.6 中断处理过程

1.6.1 保护断点现场

① 将标志寄存器FLAGS压栈,然后清除标志寄存器的IF位和TF位

② 将当前的CS & IP寄存器值压栈

1.6.2 执行中断处理程序

① 处理器将拿到的中断号乘以4,得到该中断向量在中断向量表中的偏移地址

② 从中断向量表中依次取出中断处理程序的偏移地址和段地址,并分别传送到IP & CS,从来开始执行中断处理程序

1.6.3 返回断点继续执行

① 所有中断处理程序的最后一条指令必须是中断返回指令iret(interrupt return)

② iret指令将导致处理器依次从栈中弹出IP、CS和FLAGS的原始内容,于是返回断点继续执行

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

说明1:进入中断时关中断

进入中断时,处理器将IF标志位清除,因此在中断处理过程中,处理器将不再响应可屏蔽中断。如果希望更高优先级的中断嵌套,可以在编写中断处理程序时,适时用sti指令打开中断

也就是说,计算机在硬件层面上是支持中断嵌套的

说明2:关于TF标志位

TF标志位为跟踪标志位,用于标识处理器是否允许单步中断,以进行程序调试。当TF = 0时,处理器处于正常状态;当TF = 1时,处理器处于单步状态,每执行一条指令就自动产生一次单步中断,处理器的debug功能依赖单步调试功能

这也是为什么进入中断后,必须将TF标志清除的原因。因为单步中断处理程序也是一系列指令,如果不清除TF标志,在执行中断处理程序的指令时又会触发单步中断,从而导致CPU永远执行单步中断处理程序的第1条指令

说明3:中断处理程序需要保持栈平衡

iret指令的本质是将当前栈中的3个元素按序弹出到IP、CS和FLAGS,因此中断处理程序需要在执行前后保持栈平衡,否则中断无法正确返回

说明4:NMI中断处理

NMI中断发生时,处理器不会从外部获得中断号,而是自动生成中断号2,其他处理过程与可屏蔽中断相同

2. RTC中断布署实例

2.1 RTC概述

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

① 在外围设备控制芯片ICH内部,集成了实时时钟电路(Real Time Clock,RTC)和两小块由CMOS材料制成的静态存储器(CMOS RAM)

② 实时时钟电路负责计时,而日期和时间的数值存储在CMOS RAM中

③ 由于使用主板上的电池供电,即使关闭计算机,RTC仍可计时。RTC为整台计算机提供一个基准时间,为所有需要时间的软件和硬件服务

④ 除了日期和时间的保存功能外,RTC芯片还可以提供闹钟和周期性中断的功能

⑤ RTC芯片由一个振荡频率为32.768KHz的晶振驱动,经过分频后,供RTC使用

说明:早期的RTC为独立芯片,后续集成至ICH中

2.2 CMOS RAM

2.2.1 CMOS RAM中的信息

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

① CMOS RAM通常有64 / 128 / 256B,其中日期和时间仅占用了很小一部分容量。如上图所示,CMOS RAM中前10B为日期和时间,之后为4个RTC功能寄存器

② CMOS RAM的其他空间中保存了整机的配置信息,比如各种硬件的类型和工作参数、开机密码和辅助存储设备的启动顺序等

这些参数的修改通常在BIOS SETUP开机程序中进行

2.2.2 如何访问CMOS RAM

CMOS RAM的访问,通常通过两个端口进行,其中,

① 0x70或0x74为索引端口,用来指定CMOS RAM内的单元

② 0x71或0x75为数据端口,用来读写相应单元中的内容

; 示例:读取日期
; 日期位于CMOS RAM中的0x07处
mov al, 0x07
out 0x70, al
in al, 0x71

说明:端口0x70的bit 7可用于屏蔽NMI中断(历史遗留用法)

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

2.3 RTC控制寄存器说明

2.3.1 寄存器A

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

2.3.2 寄存器B

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

说明1:RTC电路可以产生如下3种中断信号

① 周期性中断(根据寄存器A中设置的速率发生)

② 更新周期结束中断(当寄存器B的SET为0且分频电路正确配置的情况下,每秒发生一次)

③ 闹钟中断(当RTC时间到达闹钟点时发生)

说明2:如果寄存器A的bit[3:0]为置为0b000,则PIE被自动置为0,即不允许中断

2.3.3 寄存器C(只读)

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

说明1:区分RTC的3种中断

RTC电路可以产生3种中断,但是RTC到CPU的中断线只有一根,因此当CPU被触发RTC中断时,可以通过读取寄存器C来区分子中断源

说明2:寄存器C中的中断标志位均为读清,如果在RTC中断发生后不读取该寄存器清除中断标志,RTC看到中断未被处理,将不再产生中断信号

2.3.4 寄存器D

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

2.4 安装0x70号中断过程

2.4.1 设置段寄存器

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

如上一章笔记所述,从加载器跳转到用户程序之后,CS已经更新指向用户程序的代码段,因此此处只需要设置SS & DS

说明:栈设置过程中禁止中断

由于栈的设置涉及SS & SP两个寄存器,因此当Intel处理器执行任何一条改变栈段寄存器SS的指令时,会在下一条指令执行完成期间禁止中断,以避免栈设置过程被打断

因此在代码中,应该在修改段寄存器SS的指令之后,紧接着一条修改栈指针SP的指令

2.4.2 修改0x70中断向量

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

在实模式下,每个中断向量占用4B,其中低2B为中断处理程序偏移地址,高2B为中断处理程序段地址,且中断号从0开始。因此0x70 * 4,即可得到RTC中断在中断向量表中的offset

说明:在安装新的中断时,需要先关闭中断

2.4.3 RTC & 中断控制器设置

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

需要在中断源、中断控制器、处理器这3个阶段分别使能中断,才能使得相应中断被响应

2.4.4 使处理器进入低功耗状态

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

hlt指令使处理器停止执行指令,并处于停机状态,这将降低处理器的功耗。处于停机状态的处理器可以被外部中断唤醒并恢复执行,而且会继续执行hlt后面的指令

而之前jmp $构成死循环的方式,则会使得CPU占用率极高

说明:not指令

此处使用not指令反转@字符的显示属性,用于观察中断触发的频率。其中,not指令格式如下,

not r/m

not指令不影响任何标志位

2.5 RTC中断处理程序分析

2.5.1 保护中断现场

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

将中断处理程序中使用到的寄存器压栈保护

2.5.2 读取RTC时间

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

将读取到的RTC时间值压栈保存,压栈时为秒分时,后续出站时正好是时分秒

说明:test指令

test指令格式如下,操作数宽度必须一致

test r/m, r/imm

test指令将两个操作数相与,并根据结果设置标志寄存器,但是运算结果被丢弃,不改变两个操作数的内容

2.5.3 读清中断标志

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

2.5.4 显示RTC时间

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

说明1:此处反转冒号字符的显示属性,是用于观察更新结束中断是否每秒发生一次

说明2:BCD码转ASCII码

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

AL中的BCD码表示2位十进制数,因此需要分别转换并保存

2.5.5 向8259发送EOI

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

说明:8259处理中断流程

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

① 中断请求

当8259中断引脚上有一个或多个中断请求信号到来时,中断请求寄存器IRR中相应的比特位被置位

此时如果中断屏蔽寄存器IMR中对应的位被置位,则该中断被屏蔽,不会被送到优先级解析器中;如果中断没有被IMR屏蔽,则被送入中断优先级解析器

此时可以有多个中断请求被送入中断优先级解析器

② 中断判优

中断优先级解析器根据设置的规则选择优先级最高的中断请求,送往CPU

此时8259会向CPU发送一个INT信号

③ 中断响应

CPU会在执行完当前的一条指令后,向8259发送一个INTA(Interrupt Acknowledge)信号来响应中断

8259在接收到这个响应信号之后,就会将所选的最高优先级中断请求保存在正在服务寄存器ISR中

与此同时,中断请求寄存器IRR中的对应比特位被清除,表示该中断请求开始被处理

④ 获取中断号

CPU向8259发送第2个INTA信号,用于通知8259送出中断号。在该脉冲信号期间,8259会将一个代表中断号的8位数据送到数据总线上供CPU读取

⑤ 中断处理

也就是本章主要探讨的内容

⑥ 中断结束

如果8259被设置为自动结束中断(AEOI,Automatic End Of Interrupt)方式,那么在第2个INTA信号的结尾处,正在服务寄存器ISR中的对应比特位将被清除

如果8259使用非自动结束方式,那么在中断服务程序结束时,程序需要向8259发送一个结束中断(EOI)命令以恢复ISR中的比特位。如果中断请求来自级联的8259从片,那么就需要向2个8259都发送EOI命令

此后,8259就会去判断下一个最高优先级的中断,并重复上述过程

参考资料:

https://blog.csdn.net/longintchar/article/details/79439466

2.5.6 中断返回

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

恢复中断现场,并使用iret指令恢复CS & IP,使执行流回到程序断点处

2.6 上机验证

在Bochs虚拟机上运行时,需要做如下设置,时间值才能正确

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

在VirtualBox虚拟机上运行时,也需要做相应设置,时间值才能正确

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

从运行结果看,@字符的闪烁速度远快于冒号字符,说明把处理器从停机状态唤醒的不只是RTC的更新结束中断,还有其他中断

3. 内部中断与软中断

3.1 内部中断

3.1.1 内部中断含义

内部中断发生在处理器内部,是由执行指令引起的

3.1.2 内部中断示例

① 当检测到div除数为0时,或者触发结果溢出时,将产生0号中断

② 当处理器遇到非法指令时,将产生6号中断

3.1.3 内部中断特性

内部中断不受IF标志位影响,也不需要中断识别总线周期,他们是中断类型固定的,可以立即转入相应的处理过程

3.2 软中断

软中断是由int指令引起的中断处理,这类中断也不需要中断识别总线周期,中断号在指令中给出。int指令格式如下,

int3 ;断点中断指令,机器码为0xCC
int imm8 ;中断号范围为0 ~ 255
into ; 溢出中断指令

经过验证,软中断也不受IF标志位影响

说明1:int3指令的应用

int3指令一般用于断点调试,所谓断点就是某条指令的起始地址。使用int3指令实现调试的步骤如下,

① 当需要设置断点时,将断点处指定的第1个字节改为0xCC,并保存原字节

② 当处理器执行到int3时,即发生3号中断,转去执行中断处理程序。我们可以在中断处理程序中,先将所有相关寄存器和内存单元压栈,之后就可以分别取出予以显示,他们就是中断前的现场内容

③ 恢复断点处指令的第1个字节,并恢复栈,最后调用iret指令返回

说明2:into指令的应用

当处理器执行这条指令时,如果OF标志为1,则产生4号中断;否则,这条指令什么都不做

3.3 BIOS中断

3.3.1 使用中断提供服务的好处

① 如果以过程的形式提供服务,那么无论使用jmp指令还是call指令,都需要知道过程所在的段地址和偏移地址。但是操作系统提供的功能以及自身的部署是变动的,因此无法确保服务的位置不变

② 使用中断提供服务,则不需要知道目标程序的入口地址。每次操作系统加载完成后,部署好中断向量,用户即可使用相关服务

3.3.2 BIOS中断的部署

BIOS中断是软中断的一种,之所以称为BIOS中断,是因为这些中断是在计算机上电之后,由BIOS程序建立的。也就是说,这些中断功能在加载和执行主引导扇区之前就已经可以使用了

BIOS中断有如下2个来源,

① 系统BIOS

系统BIOS本身会为一些简单的外设提供初始化代码和功能调用代码,并填写中断向量表

② 外设接口卡BIOS

有些外设接口卡(e.g. 网卡、显卡)都有自己的ROM,类似于BIOS,这些ROM中提供了他自己的功能调用例程,以及本设备的初始化代码。按照规范,布局如下,

前2个单元:0x55和0xAA

第3个单元:本ROM中以512B为单位的代码长度

第4个单元及之后:实际的ROM代码

说明:外设接口卡BIOS搜索过程

外设接口卡的ROM被映射到地址空间中,在计算机启动期间,系统BIOS会以2KB为单位搜索内存地址0xC0000 ~ 0xE0000的区域,当发现某个区域以0x55AA开头时,就表示该区域包含有效的ROM代码

之后对该区域做累加和检查,看结果是否和第3个单元相符,如果相符,就从第4个单元进入执行。此时处理器执行的是硬件自带的程序指令,这些执行初始化外部设备的相关寄存器和工作状态,并填写相关的中断向量表,使他们指向自带的中断处理程序

3.3.3 BIOS中断使用示例

BIOS中断提供的服务可参考如下链接

http://www.ablmcc.edu.hk/~scy/CIT/8086_bios_and_dos_interrupts.htm

我们的示例中,使用如下2个BIOS中断

① int 0x10/0x0e

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

该中断用于在屏幕上的光标位置写一个字符,并推进光标位置

② int 0x16/0x00

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

该中断用于从键盘读取一个字符

结合这2个BIOS中断,就可以实现从键盘读入字符并显示

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

说明:课程源码中,最后调用int 0x10/0x0e中断时设置BL寄存器是没有必要的,因为可以设置显示属性的中断是int 0x10/0x09

X86汇编语言从实模式到保护模式08:中断和动态时钟显示

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

上一篇:nrf52832 利用app_timer 产生精确的1ms 心跳


下一篇:uni-app rtc插件集成指南及常见问题--iOS