x86 汇编/可编程中断控制器
最初的 IBM PC 包含一个名为 **可编程中断控制器** 的芯片,用于处理来自系统的传入中断请求,并以有序的方式将它们发送到 MPU 进行处理。最初的中断控制器是 8259A 芯片,虽然现代计算机将使用更新的变体。最常见的替代品是 APIC (高级可编程中断控制器),它本质上是旧 PIC 芯片的扩展版本,以保持向后兼容性。本页将介绍 8259A。
8259A 的功能实际上很简单。每个 PIC 有 8 个输入线,称为中断请求 (IRQ),编号从 0 到 7。当这些线之一变为高电平时,PIC 会提醒 CPU 并发送相应的中断号。这个数字是通过将 IRQ 编号 (0 到 7) 加到内部 "向量偏移" 值来计算的。CPU 使用这个值来执行相应的中断服务例程。(有关更多信息,请参阅 高级中断)。
当然,这并不像看起来那么简单,因为每个系统都有两个 PIC,一个 "主" 和一个 "从"。所以,当从机发出中断时,它实际上是发送到主机的,主机再将它发送到 CPU。这样,中断就会级联,处理器可以有 16 个 IRQ 线。在这 16 个 IRQ 线中,有一个是用于两个 PIC 芯片相互通信的,因此可用 IRQ 的数量减少到 15 个。
虽然 cli 和 sti 可用于禁用和启用 *所有* 硬件中断,但有时需要有选择地禁用来自某些设备的中断。为此,PIC 有一个内部 8 位寄存器,称为中断屏蔽寄存器 (IMR)。该寄存器中的位决定哪些 IRQ 传递到 CPU。如果一个 IRQ 被触发,但 IMR 中的对应位被设置,则它会被忽略,不会发送任何内容到 CPU。
在 15 个可用的 IRQ 中,一些与一种类型的设备普遍相关联
- IRQ 0 ‒ 系统定时器
- IRQ 1 — 键盘控制器
- IRQ 3 — 串行端口 COM2
- IRQ 4 — 串行端口 COM1
- IRQ 5 — 行式打印终端 2
- IRQ 6 — 软盘控制器
- IRQ 7 — 行式打印终端 1
- IRQ 8 — RTC 定时器
- IRQ 9 - X86_Assembly/ACPI
- IRQ 12 — 鼠标控制器
- IRQ 13 — 数学协处理器
- IRQ 14 — ATA 通道 1
- IRQ 15 — ATA 通道 2
系统中的两个 PIC 各自分配一个命令端口和一个数据端口
PIC1 | PIC2 | |
---|---|---|
命令 | 0x20 | 0xA0 |
数据 | 0x21 | 0xA1 |
通常,从数据端口读取会返回 IMR 寄存器(见上文),写入到它会设置该寄存器。我们可以利用这一点来设置哪些 IRQ 应该被忽略。例如,要禁用 IRQ 6(软盘控制器)触发
in ax, 0x21
or ax, (1 << 6)
out 0x21, ax
同样,要禁用 IRQ 12(鼠标控制器)
in ax, 0xA1
or ax, (1 << 4) ;IRQ 12 is actually IRQ 4 of PIC2
out 0xA1, ax
另一个常见任务,通常在操作系统初始化期间执行,是重映射 PIC。也就是说,改变它们的内部向量偏移量,从而改变它们发送的中断号。PIC1 的初始向量偏移量为 8,因此它会触发中断号 8 到 15。不幸的是,低 32 个中断中的一些被 CPU 用于异常(除零、页面错误等),导致硬件中断和软件中断之间发生冲突。对此的通常解决方案是将 PIC1 重映射到从 32 开始,并且通常将 PIC2 紧随其后从 40 开始。这需要完全重新启动 PIC,但实际上并不困难,只需八个 out
即可完成。
mov al, 0x11
out 0x20, al ;restart PIC1
out 0xA0, al ;restart PIC2
mov al, 0x20
out 0x21, al ;PIC1 now starts at 32
mov al, 0x28
out 0xA1, al ;PIC2 now starts at 40
mov al, 0x04
out 0x21, al ;setup cascading
mov al, 0x02
out 0xA1, al
mov al, 0x01
out 0x21, al
out 0xA1, al ;done!