欢迎光临
我们一直在努力

操作系统学习14 IDT + PIC + 键盘中断

操作系统学习14 IDT + PIC + 键盘中断

  • 一、本章说明
    • 1. 前置知识
    • 2. 本章目标
    • 3. 目录结构
    • 4. 与 lesson13 的差异
  • 二、IDT:中断来了跳去哪
    • 1. 为什么需要 IDT
      • 中断门描述符(Interrupt Gate)的硬件细节
      • 门描述符里保存:
    • 2. 向量号布局
    • 3. 代码:注册 IRQ1 门描述符
  • 三、8259A 可编程中断控制器(PIC)
    • 1. 8259A 是什么
    • 2. 主从级联:最多 15 路 IRQ
    • 3. IRQ 线 vs 中断向量号
    • 3.1 为什么必须「重映射」(本章最核心)
      • 致命隐患:向量号冲突
      • 解药:ICW 四步握手,整体后移到 32~47
    • 4. 端口与寄存器组
      • 4.1 两类控制字
        • ICW 的核心功能与分类
        • ICW 与 OCW 的区别
      • 4.2 内部关键寄存器(不必直接读,理解即可)
    • 5. 初始化命令字 ICW1 ~ ICW4
      • ICW1 — 启动初始化(写命令端口)
      • ICW2 — 中断向量基址(写数据端口)
      • ICW3 — 级联关系(写数据端口)
      • ICW4 — 附加模式(写数据端口)
      • 初始化后立刻设置 IMR
    • 6. 完整 init_pic 与 ICW 对照
    • 7. 一次键盘中断的完整流程
    • 8. EOI(End Of Interrupt)
      • 非特定 EOI(本章使用)
      • 特定 EOI(了解)
      • 从片 IRQ 的 EOI
    • 9. 运行期 OCW 操作(本章未用,供扩展)
    • 10. BIOS 默认 vs 本章重映射
  • 四、8253 可编程间隔定时器(PIT)
    • 1. 8253 是什么
    • 2. 输入时钟与 3 个通道
      • 2.1 输入频率
      • 2.2 三个通道在 PC 上的用途
    • 3. I/O 端口
    • 4. 控制字(Control Word)格式
    • 5. 六种工作模式(MODE 0~5)
    • 6. 初始化 Channel 0 示例(下一章参考)
    • 7. 常用分频对照表
    • 8. Channel 2 与 PC 扬声器(了解)
    • 9. 一次 IRQ0 定时中断的流程
    • 10. 本章与 8253 的关系
  • 五、8042 与键盘中断使能
  • 六、IRQ1 处理程序与环形缓冲
    • 1. 为何用缓冲
    • 2. irq1_handler
    • 3. read_char:缓冲优先,端口兜底
  • 七、kernel.c
  • 八、编译与运行
    • 按键无反应排查
      • 0. 你的日志说明什么
      • 1. 先排除低级问题
      • 2. 用断点定位「中断有没有触发」
      • 3. 对照 lesson13(最快二分)
      • 4. 查 PIC 与向量号(最常见根因)
      • 5. 查 8042 是否有数据(中断以外的证据)
      • 6. 现象对照
  • 九、常见问题
  • 十、PIC_REMAP 必须为 1:踩坑与修复
    • 1. 问题现象
    • 2. 根因(一句话)
    • 3. 修复
    • 4. 教训总结
  • 十一、本章小结

在这里插入图片描述

一、本章说明

1. 前置知识

请先阅读:

本章在 lesson13 基础上,把键盘输入从 CPU 空转轮询 升级为 IRQ1 硬件中断驱动。

2. 本章目标

完成本章后,我们将能够:

  • 构造 IDT(中断描述符表),用 LIDT 告诉 CPU 中断向量对应的处理程序
  • 初始化 8259A PIC(ICW1~4 重映射、IMR 屏蔽),把 IRQ 映射到向量 32~47
  • 编写 IRQ1 键盘中断处理程序:读 0x60 扫描码、写入环形缓冲、向 PIC 发 EOI
  • 在 init_keyboard 末尾 STI 开中断,read_char 用 HLT 休眠 等待按键,不再占用 CPU
  • 理解 8253 PIT 三通道、端口 0x40~0x43 及与 PIC IRQ0 的关系(本章屏蔽 IRQ0,下一章启用)

3. 目录结构

lesson14/
├── Makefile
├── boot/
│ ├── loader.s # 与 lesson13 相同:读盘 + 进保护模式
│ └── pm.inc # 段/门描述符宏
├── kernel/
│ ├── kernel.c # 欢迎语 + 提示符 + 键盘回显(同 lesson13)
│ ├── utilities.s # IDT + PIC + IRQ1 + 环形缓冲
│ └── utilities.h
└── README.md

4. 与 lesson13 的差异

项目lesson13lesson14
读键方式 循环 IN 0x64 轮询 IRQ1 中断 + 环形缓冲
IDT 256 项 IDT,注册向量 33
PIC 8259 重映射 + 仅开放 IRQ1
CPU 等待 空转轮询 .wait 缓冲优先;空则读 8042(Bochs 兜底)
8042 0xAE + 0xF4 额外 置命令字节 bit0 允许键盘中断
kernel.c 相同 相同

#mermaid-svg-qRNQZttBTfYwLBnd{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qRNQZttBTfYwLBnd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qRNQZttBTfYwLBnd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qRNQZttBTfYwLBnd .error-icon{fill:#552222;}#mermaid-svg-qRNQZttBTfYwLBnd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qRNQZttBTfYwLBnd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qRNQZttBTfYwLBnd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qRNQZttBTfYwLBnd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qRNQZttBTfYwLBnd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qRNQZttBTfYwLBnd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qRNQZttBTfYwLBnd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qRNQZttBTfYwLBnd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qRNQZttBTfYwLBnd .marker.cross{stroke:#333333;}#mermaid-svg-qRNQZttBTfYwLBnd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qRNQZttBTfYwLBnd p{margin:0;}#mermaid-svg-qRNQZttBTfYwLBnd .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-qRNQZttBTfYwLBnd .cluster-label text{fill:#333;}#mermaid-svg-qRNQZttBTfYwLBnd .cluster-label span{color:#333;}#mermaid-svg-qRNQZttBTfYwLBnd .cluster-label span p{background-color:transparent;}#mermaid-svg-qRNQZttBTfYwLBnd .label text,#mermaid-svg-qRNQZttBTfYwLBnd span{fill:#333;color:#333;}#mermaid-svg-qRNQZttBTfYwLBnd .node rect,#mermaid-svg-qRNQZttBTfYwLBnd .node circle,#mermaid-svg-qRNQZttBTfYwLBnd .node ellipse,#mermaid-svg-qRNQZttBTfYwLBnd .node polygon,#mermaid-svg-qRNQZttBTfYwLBnd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qRNQZttBTfYwLBnd .rough-node .label text,#mermaid-svg-qRNQZttBTfYwLBnd .node .label text,#mermaid-svg-qRNQZttBTfYwLBnd .image-shape .label,#mermaid-svg-qRNQZttBTfYwLBnd .icon-shape .label{text-anchor:middle;}#mermaid-svg-qRNQZttBTfYwLBnd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qRNQZttBTfYwLBnd .rough-node .label,#mermaid-svg-qRNQZttBTfYwLBnd .node .label,#mermaid-svg-qRNQZttBTfYwLBnd .image-shape .label,#mermaid-svg-qRNQZttBTfYwLBnd .icon-shape .label{text-align:center;}#mermaid-svg-qRNQZttBTfYwLBnd .node.clickable{cursor:pointer;}#mermaid-svg-qRNQZttBTfYwLBnd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qRNQZttBTfYwLBnd .arrowheadPath{fill:#333333;}#mermaid-svg-qRNQZttBTfYwLBnd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qRNQZttBTfYwLBnd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qRNQZttBTfYwLBnd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qRNQZttBTfYwLBnd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qRNQZttBTfYwLBnd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qRNQZttBTfYwLBnd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qRNQZttBTfYwLBnd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qRNQZttBTfYwLBnd .cluster text{fill:#333;}#mermaid-svg-qRNQZttBTfYwLBnd .cluster span{color:#333;}#mermaid-svg-qRNQZttBTfYwLBnd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qRNQZttBTfYwLBnd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qRNQZttBTfYwLBnd rect.text{fill:none;stroke-width:0;}#mermaid-svg-qRNQZttBTfYwLBnd .icon-shape,#mermaid-svg-qRNQZttBTfYwLBnd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qRNQZttBTfYwLBnd .icon-shape p,#mermaid-svg-qRNQZttBTfYwLBnd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qRNQZttBTfYwLBnd .icon-shape .label rect,#mermaid-svg-qRNQZttBTfYwLBnd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qRNQZttBTfYwLBnd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qRNQZttBTfYwLBnd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qRNQZttBTfYwLBnd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

lesson14 中断

按键

8042 OBF=1

PIC IRQ1

CPU 查 IDT 向量33

irq1_handler 读0x60入缓冲

EOI → STI 后 HLT 唤醒

read_char 从缓冲取键

lesson13 轮询

IN 0x64

否 空转

read_char

OBF=1?

IN 0x60


二、IDT:中断来了跳去哪

1. 为什么需要 IDT

在实模式下,中断发生时 CPU 会去内存前 1KB 的 IVT(中断向量表) 寻找 4 字节的 CS:IP。但进入保护模式后,IVT 宣告失效,取而代之的是 IDT(Interrupt Descriptor Table)。

中断门描述符(Interrupt Gate)的硬件细节

IDT 数组中存放的每一项都是一个 8 字节(64位) 的门描述符。其硬件内存布局如下:

31 16 15 14 13 12 11 8 7 5 4 0
+———————+—+—–+—+—–+—+—+——–+
| Offset 31:16 | P | DPL | 0 | Type| 0 | 0 |Reserved|32
+———————+—+—–+—+—–+—+—+——–+
| Segment Selector | Offset 15:0 |32
+———————+————————————+

  • Offset (31:0):中断处理程序(ISR)在内存中的 32 位绝对物理地址(分为高 16 位和低 16 位拼凑)。
  • Segment Selector (15:0):目标代码段的选择子。在平坦模型内核中,这里恒为内核代码段选择子 0x08(GDT 的第 1 项)。
  • Type (11:8):对于 32 位中断门,此字段固定为 0xE(即二进制 1110)。
  • DPL (14:13):特权级。内核中断设为 00。当发生硬件中断时,即使当前 CPU 处于用户态(DPL=3),也能无缝切入内核态执行。
  • P (15):有效位。置 1 表示该中断门有效。

门描述符里保存:

字段含义
目标偏移 处理程序入口 EIP
段选择子 处理程序所在代码段(平坦模式下为 8)
类型 0x8E = 32 位中断门(进入 ISR 时自动清 IF)

用 LIDT 加载 IDTR(基址 + 界限),与 LGDT 加载 GDT 类似。

2. 向量号布局

范围用途
0 ~ 31 CPU 保留异常(#DE、#GP、#PF …)
32 ~ 47 常见 OS 把 8259 PIC 的 IRQ0~15 映射到此
48 ~ 255 软件中断、其他设备

IRQ 与向量不是同一个概念: 键盘占 IRQ1 这条硬件线;经 PIC 重映射后,对应 中断向量 33(0x20 + 1)。8259A 的端口、ICW/OCW、IMR、EOI 等详见 第三节。

3. 代码:注册 IRQ1 门描述符

utilities.s 中预留 256×8 字节的 idt,只填写向量 33 一项:

SELECTOR_CODE equ 8
IRQ1_VECTOR equ 33 ; PIC 重映射后 IRQ1 → 0x21

idt: times 256 * 8 db 0
idt_ptr:
dw idt_end – idt – 1
dd idt

init_idt:
MOV EAX, irq1_handler
MOV WORD [idt + IRQ1_VECTOR * 8 + 0], AX ; 偏移低 16 位
MOV WORD [idt + IRQ1_VECTOR * 8 + 2], SELECTOR_CODE
MOV BYTE [idt + IRQ1_VECTOR * 8 + 5], 0x8E ; 32 位中断门
SHR EAX, 16
MOV WORD [idt + IRQ1_VECTOR * 8 + 6], AX ; 偏移高 16 位
LIDT [idt_ptr]
RET

pm.inc 中的 Gate 宏 可静态生成门描述符;本章仅注册一项,直接在内存里写字段更直观。


三、8259A 可编程中断控制器(PIC)

1. 8259A 是什么

8259A(Programmable Interrupt Controller,可编程中断控制器)是 IBM PC/AT 时代的经典芯片,用来管理 外部设备中断。

CPU 只有一条 INTR 中断请求输入线,外设却有键盘、时钟、磁盘、串口等多路信号。8259A 的作用就是:

  • 汇聚 多条 IRQ 线;
  • 按 优先级 决定先响应哪一路;
  • 向 CPU 拉 INTR;
  • CPU 响应后,通过 INTA 周期告知 IRQ 编号;
  • OS 把 IRQ 编号换算成 中断向量号,再查 IDT 跳转。
  • #mermaid-svg-ivjDLQUHTYEdX3TE{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ivjDLQUHTYEdX3TE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ivjDLQUHTYEdX3TE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ivjDLQUHTYEdX3TE .error-icon{fill:#552222;}#mermaid-svg-ivjDLQUHTYEdX3TE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ivjDLQUHTYEdX3TE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ivjDLQUHTYEdX3TE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ivjDLQUHTYEdX3TE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ivjDLQUHTYEdX3TE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ivjDLQUHTYEdX3TE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ivjDLQUHTYEdX3TE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ivjDLQUHTYEdX3TE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ivjDLQUHTYEdX3TE .marker.cross{stroke:#333333;}#mermaid-svg-ivjDLQUHTYEdX3TE svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ivjDLQUHTYEdX3TE p{margin:0;}#mermaid-svg-ivjDLQUHTYEdX3TE .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ivjDLQUHTYEdX3TE .cluster-label text{fill:#333;}#mermaid-svg-ivjDLQUHTYEdX3TE .cluster-label span{color:#333;}#mermaid-svg-ivjDLQUHTYEdX3TE .cluster-label span p{background-color:transparent;}#mermaid-svg-ivjDLQUHTYEdX3TE .label text,#mermaid-svg-ivjDLQUHTYEdX3TE span{fill:#333;color:#333;}#mermaid-svg-ivjDLQUHTYEdX3TE .node rect,#mermaid-svg-ivjDLQUHTYEdX3TE .node circle,#mermaid-svg-ivjDLQUHTYEdX3TE .node ellipse,#mermaid-svg-ivjDLQUHTYEdX3TE .node polygon,#mermaid-svg-ivjDLQUHTYEdX3TE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ivjDLQUHTYEdX3TE .rough-node .label text,#mermaid-svg-ivjDLQUHTYEdX3TE .node .label text,#mermaid-svg-ivjDLQUHTYEdX3TE .image-shape .label,#mermaid-svg-ivjDLQUHTYEdX3TE .icon-shape .label{text-anchor:middle;}#mermaid-svg-ivjDLQUHTYEdX3TE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ivjDLQUHTYEdX3TE .rough-node .label,#mermaid-svg-ivjDLQUHTYEdX3TE .node .label,#mermaid-svg-ivjDLQUHTYEdX3TE .image-shape .label,#mermaid-svg-ivjDLQUHTYEdX3TE .icon-shape .label{text-align:center;}#mermaid-svg-ivjDLQUHTYEdX3TE .node.clickable{cursor:pointer;}#mermaid-svg-ivjDLQUHTYEdX3TE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ivjDLQUHTYEdX3TE .arrowheadPath{fill:#333333;}#mermaid-svg-ivjDLQUHTYEdX3TE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ivjDLQUHTYEdX3TE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ivjDLQUHTYEdX3TE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ivjDLQUHTYEdX3TE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ivjDLQUHTYEdX3TE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ivjDLQUHTYEdX3TE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ivjDLQUHTYEdX3TE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ivjDLQUHTYEdX3TE .cluster text{fill:#333;}#mermaid-svg-ivjDLQUHTYEdX3TE .cluster span{color:#333;}#mermaid-svg-ivjDLQUHTYEdX3TE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ivjDLQUHTYEdX3TE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ivjDLQUHTYEdX3TE rect.text{fill:none;stroke-width:0;}#mermaid-svg-ivjDLQUHTYEdX3TE .icon-shape,#mermaid-svg-ivjDLQUHTYEdX3TE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ivjDLQUHTYEdX3TE .icon-shape p,#mermaid-svg-ivjDLQUHTYEdX3TE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ivjDLQUHTYEdX3TE .icon-shape .label rect,#mermaid-svg-ivjDLQUHTYEdX3TE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ivjDLQUHTYEdX3TE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ivjDLQUHTYEdX3TE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ivjDLQUHTYEdX3TE :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    IRQ1

    IRQ0

    IRQ6

    INTR

    INTA

    查 IDT

    键盘 8042

    8259A PIC

    时钟 8253

    软盘

    x86 CPU

    中断处理程序

    历史说明: 现代多核 PC 已普遍改用 APIC / IOAPIC,8259A 在物理机上多由芯片组模拟或仅用于兼容模式。但在 Bochs、QEMU 教学环境以及《30天自制操作系统》等入门路线中,8259A 仍是理解 「外设 IRQ → 向量号 → IDT」 的最佳起点。


    2. 主从级联:最多 15 路 IRQ

    一片 8259A 有 8 条 IRQ 输入(IRQ0~IRQ7)。PC/AT 通过 级联 再挂第二片,形成 主片 + 从片:

    芯片I/O 端口管理 IRQ
    主片(Master) 命令 0x20 / 数据 0x21 IRQ0~7
    从片(Slave) 命令 0xA0 / 数据 0xA1 IRQ8~15

    从片的 INTR 输出接到 主片的 IRQ2,因此主片 IRQ2 被占用为「级联线」,实际可用 15 路 外设中断(IRQ01、37、8~15)。

    #mermaid-svg-1SIVQVyf2aj5iWix{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1SIVQVyf2aj5iWix .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1SIVQVyf2aj5iWix .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1SIVQVyf2aj5iWix .error-icon{fill:#552222;}#mermaid-svg-1SIVQVyf2aj5iWix .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1SIVQVyf2aj5iWix .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1SIVQVyf2aj5iWix .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1SIVQVyf2aj5iWix .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1SIVQVyf2aj5iWix .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1SIVQVyf2aj5iWix .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1SIVQVyf2aj5iWix .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1SIVQVyf2aj5iWix .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1SIVQVyf2aj5iWix .marker.cross{stroke:#333333;}#mermaid-svg-1SIVQVyf2aj5iWix svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1SIVQVyf2aj5iWix p{margin:0;}#mermaid-svg-1SIVQVyf2aj5iWix .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-1SIVQVyf2aj5iWix .cluster-label text{fill:#333;}#mermaid-svg-1SIVQVyf2aj5iWix .cluster-label span{color:#333;}#mermaid-svg-1SIVQVyf2aj5iWix .cluster-label span p{background-color:transparent;}#mermaid-svg-1SIVQVyf2aj5iWix .label text,#mermaid-svg-1SIVQVyf2aj5iWix span{fill:#333;color:#333;}#mermaid-svg-1SIVQVyf2aj5iWix .node rect,#mermaid-svg-1SIVQVyf2aj5iWix .node circle,#mermaid-svg-1SIVQVyf2aj5iWix .node ellipse,#mermaid-svg-1SIVQVyf2aj5iWix .node polygon,#mermaid-svg-1SIVQVyf2aj5iWix .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1SIVQVyf2aj5iWix .rough-node .label text,#mermaid-svg-1SIVQVyf2aj5iWix .node .label text,#mermaid-svg-1SIVQVyf2aj5iWix .image-shape .label,#mermaid-svg-1SIVQVyf2aj5iWix .icon-shape .label{text-anchor:middle;}#mermaid-svg-1SIVQVyf2aj5iWix .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1SIVQVyf2aj5iWix .rough-node .label,#mermaid-svg-1SIVQVyf2aj5iWix .node .label,#mermaid-svg-1SIVQVyf2aj5iWix .image-shape .label,#mermaid-svg-1SIVQVyf2aj5iWix .icon-shape .label{text-align:center;}#mermaid-svg-1SIVQVyf2aj5iWix .node.clickable{cursor:pointer;}#mermaid-svg-1SIVQVyf2aj5iWix .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1SIVQVyf2aj5iWix .arrowheadPath{fill:#333333;}#mermaid-svg-1SIVQVyf2aj5iWix .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1SIVQVyf2aj5iWix .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1SIVQVyf2aj5iWix .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1SIVQVyf2aj5iWix .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1SIVQVyf2aj5iWix .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1SIVQVyf2aj5iWix .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1SIVQVyf2aj5iWix .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1SIVQVyf2aj5iWix .cluster text{fill:#333;}#mermaid-svg-1SIVQVyf2aj5iWix .cluster span{color:#333;}#mermaid-svg-1SIVQVyf2aj5iWix div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1SIVQVyf2aj5iWix .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1SIVQVyf2aj5iWix rect.text{fill:none;stroke-width:0;}#mermaid-svg-1SIVQVyf2aj5iWix .icon-shape,#mermaid-svg-1SIVQVyf2aj5iWix .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1SIVQVyf2aj5iWix .icon-shape p,#mermaid-svg-1SIVQVyf2aj5iWix .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1SIVQVyf2aj5iWix .icon-shape .label rect,#mermaid-svg-1SIVQVyf2aj5iWix .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1SIVQVyf2aj5iWix .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1SIVQVyf2aj5iWix .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1SIVQVyf2aj5iWix :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    INTR 接主片 IRQ2

    从片 8259A 端口 A0h/A1h

    IRQ8 RTC

    IRQ9 …

    IRQ15 …

    主片 8259A 端口 20h/21h

    IRQ0 时钟

    IRQ1 键盘

    IRQ2 级联从片

    IRQ3 串口2

    IRQ4 串口1

    IRQ5 并口2

    IRQ6 软盘

    IRQ7 并口1

    CPU INTR

    本章键盘走主片 IRQ1,EOI 只需写主片命令端口 0x20。


    3. IRQ 线 vs 中断向量号

    初学者容易混淆的两个概念:

    概念含义键盘例子
    IRQ 线 硬件请求线编号(8259A 输入脚) IRQ1
    中断向量号 CPU 查 IDT 用的下标(0~255) 由 PIC 配置决定

    8259A 只决定「哪条 IRQ 线对应哪个向量号」;CPU 拿到向量号后再查 IDT。因此 PIC 映射 与 IDT 注册 必须一致。


    3.1 为什么必须「重映射」(本章最核心)

    这是本章最容易让初学者遭遇 Triple Fault(三重故障 → 重启) 的知识点。

    致命隐患:向量号冲突

    IBM PC 在设计之初留下了一个历史包袱:实模式下,BIOS 默认把 8259A 主片 IRQ0~7 映射到中断向量 8~15。

    然而 Intel 在设计 80386 保护模式时规定:向量 0~31 强制保留给 CPU 内部异常(如 0 为除零、13 为 #GP、14 为 #PF)。

    硬件引脚BIOS 默认向量保护模式下 CPU 的理解灾难后果
    IRQ0(时钟) 0x08 #DF(Double Fault 双重故障) 时钟一触发,CPU 误以为发生双重故障,直接崩溃重启
    IRQ1(键盘) 0x09 协处理器段超越(历史保留异常) 敲击键盘也会撞上异常区;IDT 空门 → #GP → #DF → Triple Fault
    IRQ2(级联) 0x0A #TS(Invalid TSS) 从片相关中断同样踩雷
    11~15 #NP / #SS / #GP / #PF / 保留 任意未屏蔽 IRQ 都可能引爆

    BIOS 默认映射一览:

    IRQ默认向量号
    IRQ0 8
    IRQ1 9
    IRQ2 10
    IRQ7 15
    IRQ8 16
    IRQ15 23

    8253 PIT 以约 18.2 Hz 周期拉高 IRQ0。若不重映射就 STI,时钟中断会以向量 8 进入 IDT——而这正是 #DF 的槽位。空门描述符 → #GP → 再 #DF → Triple Fault 重启。键盘 IRQ1→9 同理。

    解药:ICW 四步握手,整体后移到 32~47

    为避开 0~31 的雷区,必须向主片端口(0x20/0x21)和从片端口(0xA0/0xA1)按固定顺序发送 ICW(Initialization Command Words),把外部中断整体映射到 向量 32~47:

    步骤命令字本章典型值作用
    1 ICW1 0x11 → 0x20/0xA0 启动初始化,级联模式,需要 ICW4
    2 ICW2 主片 0x20、从片 0x28 向量基址:IRQ0→32,IRQ8→40
    3 ICW3 主片 0x04、从片 0x02 主片 IRQ2 接从片,从片编号 2
    4 ICW4 0x01 8086 模式(x86 必备)
    之后 IMR 主片 0xFD、从片 0xFF 仅开放 IRQ1,其余全屏蔽

    重映射后的布局(本章做法,基址 0x20):

    IRQ新向量号典型设备
    IRQ0 32 8253 PIT Channel 0(系统 tick,见 第四节;本章 IMR 屏蔽)
    IRQ1 33 8042 键盘(本章开放,IDT[33] 已注册)
    IRQ2 34 级联从片
    IRQ15 47 各类外设
    向量范围用途
    0 ~ 31 CPU 保留异常(#DE、#DF、#GP、#PF 等)——外设 IRQ 绝不能落在这里
    32 ~ 47 8259A 重映射后的 IRQ0~15
    48 ~ 255 软件中断 / 其他设备

    结论: 任何进入保护模式的 OS,都必须在 STI 之前 对 8259A 执行完整的 ICW1~4 重映射。本章 PIC_REMAP equ 1,详细位域与代码见下文 §5、§6;踩坑记录见 第十节。


    4. 端口与寄存器组

    8259A 通过 4 个 I/O 端口 访问,每个端口读写含义由 命令字 决定:

    端口写读
    0x20 主片 命令(OCW/ICW 首字节) 主片 ISR/IRR(需 OCW3)
    0x21 主片 数据(ICW2~4、IMR) 主片 IMR
    0xA0 从片 命令 从片 ISR/IRR
    0xA1 从片 数据 从片 IMR

    4.1 两类控制字

    在微机原理与接口技术(特别是可编程中断控制器 8259A)中,在系统启动或复位后,要对 8259A 芯片的工作模式进行一次性配置,建立其基础的工作框架,即ICW,全称是 Initialization Command Words,中文称为初始化命令字。

    ICW 的核心功能与分类

    8259A 共有 4 个初始化命令字(ICW1 ~ ICW4),它们需要严格按照顺序写入,分别负责不同的基础配置:

    • ICW1(基本功能设置):决定中断请求的触发方式(边沿触发或电平触发)、设置是单片工作还是多片级联模式,并指示是否需要写入 ICW4。
    • ICW2(中断向量设置):用于设置中断向量基地址(中断类型码的起始地址)。CPU 收到中断后,会根据这个基地址和中断引脚的编号找到对应的中断服务程序。
    • ICW3(级联控制字):仅在多片 8259A 级联时使用。主片用它来标识哪些 IRQ 引脚连接了从片;从片用它来标识自己在级联系统中的编号。
    • ICW4(高级方式控制):用于配置更高级的系统行为,例如选择 CPU 系统模式(如 8086 模式)、设置中断结束方式(如自动 EOI)、缓冲模式以及全嵌套方式等。
    ICW 与 OCW 的区别

    与 ICW 对应的是 OCW(Operation Command Words,操作命令字)。两者的主要区别在于:

    • ICW(初始化命令字):属于一次性配置。在系统上电或复位时按严格顺序写入,决定了芯片的底层基础架构,初始化完成后一般不再改动。
    • OCW(操作命令字):属于运行时控制。在芯片初始化完成后的正常工作期间,可以随时写入,用于动态控制中断行为,例如动态屏蔽/开放某个中断(OCW1)、发送中断结束命令 EOI(OCW2)或读取内部状态等。 结合你上一轮提供的汇编代码,init_pic 函数中向 0x20 和 0x21 等端口连续写入的 0x11、0x20、0x28 等字节,正是严格按照顺序写入的 ICW1 到 ICW4,用于重新映射(Remap)主从 8259A 的中断向量基地址。
    类型名称何时使用
    ICW Initialization Command Word 初始化 时按 ICW1→ICW4 顺序写入
    OCW Operation Command Word 运行中 屏蔽 IRQ、读状态、发 EOI 等

    初始化完成后,再写 0x11 等 ICW 值会破坏芯片状态,须重新初始化。

    4.2 内部关键寄存器(不必直接读,理解即可)

    寄存器全称作用
    IRR Interrupt Request Register 外设拉高了 IRQ 线,对应 bit 置 1
    ISR In-Service Register CPU 已开始处理的中断,对应 bit 置 1
    IMR Interrupt Mask Register 1 = 屏蔽 该 IRQ,0 = 允许

    优先级(默认): IRQ0 最高,IRQ7 最低;从片 IRQ8~15 整体优先级介于主片 IRQ1 与 IRQ2 之间(具体与 ICW4 配置有关)。


    5. 初始化命令字 ICW1 ~ ICW4

    8259A 上电后必须按固定顺序初始化。本章 init_pic 对 主从各写一遍。

    ICW1 — 启动初始化(写命令端口)

    向 0x20 / 0xA0 写 0x11:

    位值含义
    D0 1 需要 ICW4
    D1 1 级联模式(非单片)
    D4 1 固定特征位,必须是 1

    写 ICW1 后,芯片进入初始化状态,自动屏蔽所有 IRQ,并准备接收后续 ICW。

    ICW2 — 中断向量基址(写数据端口)

    芯片写入端口本章值含义
    主片 0x21 0x20 IRQ0→32,IRQ1→33,…
    从片 0xA1 0x28 IRQ8→40,IRQ9→41,…

    写入的是 IRQ0 / IRQ8 对应的向量号,后续 IRQ 依次 +1。

    ICW3 — 级联关系(写数据端口)

    芯片本章值含义
    主片 0x04 bit2=1 → IRQ2 上接从片
    从片 0x02 从片在中片级联链上的编号为 2

    ICW4 — 附加模式(写数据端口)

    向 0x21 / 0xA1 写 0x01:

    位值含义
    D0 1 8086/8088 模式(必须,AT 架构)

    未设置此项时,EOI、向量格式可能与 x86 保护模式不兼容。

    初始化后立刻设置 IMR

    ICW 序列结束后,通过 IMR 决定哪些 IRQ 能进来。写 数据端口 0x21 / 0xA1:

    ; 主片:仅开放 IRQ1(键盘)
    MOV AL, 0xFD ; 二进制 11111101,bit1=0 → IRQ1 开放
    OUT 0x21, AL
    ; 从片:全部屏蔽
    MOV AL, 0xFF
    OUT 0xA1, AL

    IMR 位含义: bit N = 1 表示 屏蔽 IRQN;0 表示 允许。

    值bit1IRQ1 键盘
    0xFF 1 屏蔽
    0xFD 0 开放

    为何要屏蔽 IRQ0 等: 本章 IDT 只注册了向量 33。若 IRQ0(时钟)未屏蔽且触发,CPU 会跳到空的 IDT 项 → triple fault。


    6. 完整 init_pic 与 ICW 对照

    init_pic:
    ; ICW1
    MOV AL, 0x11
    OUT 0x20, AL ; 主片
    OUT 0xA0, AL ; 从片

    ; ICW2:向量基址
    MOV AL, 0x20
    OUT 0x21, AL ; 主片 IRQ0 → 32
    MOV AL, 0x28
    OUT 0xA1, AL ; 从片 IRQ8 → 40

    ; ICW3:级联
    MOV AL, 0x04
    OUT 0x21, AL ; 主片 IRQ2 接从片
    MOV AL, 0x02
    OUT 0xA1, AL ; 从片编号 2

    ; ICW4:8086 模式
    MOV AL, 0x01
    OUT 0x21, AL
    OUT 0xA1, AL

    ; IMR:仅开放 IRQ1
    MOV AL, 0xFD
    OUT 0x21, AL
    MOV AL, 0xFF
    OUT 0xA1, AL
    RET


    7. 一次键盘中断的完整流程

    irq1_handler

    IDT

    CPU

    8259A 主片

    8042

    按键

    irq1_handler

    IDT

    CPU

    8259A 主片

    8042

    按键

    #mermaid-svg-a97I8jZoHg08ELmf{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a97I8jZoHg08ELmf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a97I8jZoHg08ELmf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a97I8jZoHg08ELmf .error-icon{fill:#552222;}#mermaid-svg-a97I8jZoHg08ELmf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a97I8jZoHg08ELmf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a97I8jZoHg08ELmf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a97I8jZoHg08ELmf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a97I8jZoHg08ELmf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a97I8jZoHg08ELmf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a97I8jZoHg08ELmf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a97I8jZoHg08ELmf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a97I8jZoHg08ELmf .marker.cross{stroke:#333333;}#mermaid-svg-a97I8jZoHg08ELmf svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a97I8jZoHg08ELmf p{margin:0;}#mermaid-svg-a97I8jZoHg08ELmf .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-a97I8jZoHg08ELmf text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-a97I8jZoHg08ELmf .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-a97I8jZoHg08ELmf .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-a97I8jZoHg08ELmf .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-a97I8jZoHg08ELmf .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-a97I8jZoHg08ELmf #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-a97I8jZoHg08ELmf .sequenceNumber{fill:white;}#mermaid-svg-a97I8jZoHg08ELmf #sequencenumber{fill:#333;}#mermaid-svg-a97I8jZoHg08ELmf #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-a97I8jZoHg08ELmf .messageText{fill:#333;stroke:none;}#mermaid-svg-a97I8jZoHg08ELmf .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-a97I8jZoHg08ELmf .labelText,#mermaid-svg-a97I8jZoHg08ELmf .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-a97I8jZoHg08ELmf .loopText,#mermaid-svg-a97I8jZoHg08ELmf .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-a97I8jZoHg08ELmf .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-a97I8jZoHg08ELmf .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-a97I8jZoHg08ELmf .noteText,#mermaid-svg-a97I8jZoHg08ELmf .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-a97I8jZoHg08ELmf .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-a97I8jZoHg08ELmf .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-a97I8jZoHg08ELmf .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-a97I8jZoHg08ELmf .actorPopupMenu{position:absolute;}#mermaid-svg-a97I8jZoHg08ELmf .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-a97I8jZoHg08ELmf .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-a97I8jZoHg08ELmf .actor-man circle,#mermaid-svg-a97I8jZoHg08ELmf line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-a97I8jZoHg08ELmf :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    IF=1 且 STI 已执行

    扫描码

    OBF=1, 拉 IRQ1

    IRQ1 有效

    IRR bit1=1

    INTR=1

    INTA 周期

    向量号 33

    查门描述符

    跳转 irq1_handler

    IN 0x60 读扫描码

    OUT 0x20, 0x20 EOI

    IRET

  • 8042 收到扫描码,置 OBF,若命令字节 bit0=1 则向 PIC IRQ1 发请求。
  • PIC 置 IRR 相应位,若未被 IMR 屏蔽且优先级足够,向 CPU 拉 INTR。
  • CPU 完成当前指令且 IF=1,响应中断,经 INTA 从 PIC 取得 向量 33。
  • CPU 查 IDT[33],压栈 CS/EIP/EFLAGS,跳转 irq1_handler。
  • ISR 读 0x60(必须读,否则 8042 不会发下一次中断),写环形缓冲,发 EOI。
  • IRET 恢复现场;若主线程在 HLT,此时被唤醒。

  • 8. EOI(End Of Interrupt)

    中断处理程序结束前,必须通知 PIC:当前这一级 IRQ 已服务完毕,否则 ISR 位一直置位,同级或更低优先级中断可能被阻塞。

    非特定 EOI(本章使用)

    MOV AL, 0x20
    OUT 0x20, AL ; 主片:结束优先级最高的那一条 in-service 中断

    键盘 IRQ1 接 主片,写 0x20 命令端口 即可。

    特定 EOI(了解)

    若需明确结束某一条 IRQ,可写 0x20 | IRQ号 到主/从片命令端口,例如结束 IRQ1:OUT 0x20, 0x21。多 IRQ 并发、嵌套中断时才常用;本章单 IRQ 场景用 0x20 足够。

    从片 IRQ 的 EOI

    若中断来自 从片(IRQ8~15),须 先 向从片发 EOI,再 向主片发 EOI(因为 IRQ2 级联)。键盘 IRQ1 在主片,无需 写 0xA0。


    9. 运行期 OCW 操作(本章未用,供扩展)

    操作做法
    屏蔽某 IRQ 读 IMR → 置对应 bit → 写回 0x21/0xA1
    开放某 IRQ 读 IMR → 清对应 bit → 写回
    读 IRR/ISR 先写 OCW3 到命令端口,再从命令端口读

    例如开放 IRQ0 定时器(须先注册 IDT[32] 并初始化 PIT,见 第四节 8253):

    IN AL, 0x21
    AND AL, 0xFE ; 清 bit0,开放 IRQ0
    OUT 0x21, AL


    10. BIOS 默认 vs 本章重映射

    场景IRQ1 向量IDT 应注册是否写 ICW
    BIOS 实模式 / 未 remap 9 IDT[9]
    本章保护模式 OS 33 IDT[33]

    常见错误: 执行了 init_pic 重映射,IDT 却仍注册向量 9;或 未 重映射,IDT 注册 33。按键后 CPU 跳到 未注册的门 → triple fault 或「无反应」(视环境而定)。

    调试时可对照 tools/bochsout.txt:若 IF=1 且 EIP 停在 HLT,多为 8042 未开中断位 或 IMR 仍屏蔽 IRQ1;若按键立刻重启,优先查 向量号与 IDT 是否一致。


    四、8253 可编程间隔定时器(PIT)

    1. 8253 是什么

    8253(Programmable Interval Timer,可编程间隔定时器)是 PC 上负责 硬件计时 的经典芯片;后续 8254 与其 pin 兼容、功能相近,BIOS/OS 文档里常统称 PIT。

    它内部有 3 个独立的 16 位计数器(通道 Channel 0~2),对固定 输入时钟 分频,在计数到设定值时改变输出电平,或向 8259A PIC 的 IRQ0 发中断请求。

    #mermaid-svg-QuBLsb6x4aOZjZI2{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QuBLsb6x4aOZjZI2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QuBLsb6x4aOZjZI2 .error-icon{fill:#552222;}#mermaid-svg-QuBLsb6x4aOZjZI2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QuBLsb6x4aOZjZI2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QuBLsb6x4aOZjZI2 .marker.cross{stroke:#333333;}#mermaid-svg-QuBLsb6x4aOZjZI2 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QuBLsb6x4aOZjZI2 p{margin:0;}#mermaid-svg-QuBLsb6x4aOZjZI2 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-QuBLsb6x4aOZjZI2 .cluster-label text{fill:#333;}#mermaid-svg-QuBLsb6x4aOZjZI2 .cluster-label span{color:#333;}#mermaid-svg-QuBLsb6x4aOZjZI2 .cluster-label span p{background-color:transparent;}#mermaid-svg-QuBLsb6x4aOZjZI2 .label text,#mermaid-svg-QuBLsb6x4aOZjZI2 span{fill:#333;color:#333;}#mermaid-svg-QuBLsb6x4aOZjZI2 .node rect,#mermaid-svg-QuBLsb6x4aOZjZI2 .node circle,#mermaid-svg-QuBLsb6x4aOZjZI2 .node ellipse,#mermaid-svg-QuBLsb6x4aOZjZI2 .node polygon,#mermaid-svg-QuBLsb6x4aOZjZI2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QuBLsb6x4aOZjZI2 .rough-node .label text,#mermaid-svg-QuBLsb6x4aOZjZI2 .node .label text,#mermaid-svg-QuBLsb6x4aOZjZI2 .image-shape .label,#mermaid-svg-QuBLsb6x4aOZjZI2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-QuBLsb6x4aOZjZI2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QuBLsb6x4aOZjZI2 .rough-node .label,#mermaid-svg-QuBLsb6x4aOZjZI2 .node .label,#mermaid-svg-QuBLsb6x4aOZjZI2 .image-shape .label,#mermaid-svg-QuBLsb6x4aOZjZI2 .icon-shape .label{text-align:center;}#mermaid-svg-QuBLsb6x4aOZjZI2 .node.clickable{cursor:pointer;}#mermaid-svg-QuBLsb6x4aOZjZI2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QuBLsb6x4aOZjZI2 .arrowheadPath{fill:#333333;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QuBLsb6x4aOZjZI2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QuBLsb6x4aOZjZI2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QuBLsb6x4aOZjZI2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QuBLsb6x4aOZjZI2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QuBLsb6x4aOZjZI2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QuBLsb6x4aOZjZI2 .cluster text{fill:#333;}#mermaid-svg-QuBLsb6x4aOZjZI2 .cluster span{color:#333;}#mermaid-svg-QuBLsb6x4aOZjZI2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QuBLsb6x4aOZjZI2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QuBLsb6x4aOZjZI2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-QuBLsb6x4aOZjZI2 .icon-shape,#mermaid-svg-QuBLsb6x4aOZjZI2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QuBLsb6x4aOZjZI2 .icon-shape p,#mermaid-svg-QuBLsb6x4aOZjZI2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QuBLsb6x4aOZjZI2 .icon-shape .label rect,#mermaid-svg-QuBLsb6x4aOZjZI2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QuBLsb6x4aOZjZI2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QuBLsb6x4aOZjZI2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QuBLsb6x4aOZjZI2 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Channel 0

    Channel 1

    Channel 2

    输入时钟 1.193182 MHz

    8253 PIT

    PIC IRQ0

    DRAM 刷新 历史用途

    PC 扬声器 Gate

    8259A

    CPU 定时中断

    与 8259A 的关系: 8253 不产生 CPU 向量,只拉 IRQ0 线;向量号仍由 PIC 决定(BIOS 默认 8,重映射后 32)。下一章做 定时器中断 / sleep / 调度 时,通常同时配置 PIT + IDT[32] + 开放 PIC IRQ0。

    本章为何屏蔽 IRQ0: init_pic 写 IMR 0xFD 屏蔽 IRQ0,因为尚未注册 向量 32 的处理程序;若开放,约 18.2 次/秒 的 IRQ0 会触发 triple fault。


    2. 输入时钟与 3 个通道

    2.1 输入频率

    PC 上 PIT 输入时钟固定为:

    f

    in

    =

    1

    193

    182
      

    Hz

    1193182
      

    Hz

    f_{\\text{in}} = 1\\,193\\,182 \\;\\text{Hz} \\approx 1193182 \\;\\text{Hz}

    fin=1193182Hz1193182Hz

    (即 ISA 总线时钟 14.31818 MHz 经 12 分频。)

    输出频率:

    f

    out

    =

    1193182

    分频值 

    N

    f_{\\text{out}} = \\frac{1193182}{\\text{分频值 }N}

    fout=分频值 N1193182

    分频值 N 为 16 位,范围 1 ~ 65535。例如 N = 65535 时:

    f

    1193182

    65535

    18.2
      

    Hz

    f \\approx \\frac{1193182}{65535} \\approx 18.2 \\;\\text{Hz}

    f65535119318218.2Hz

    即 约每 55ms 中断一次——BIOS 默认 Channel 0 常用此量级,也是 DOS 时代 INT 08h 系统时钟 tick 的来源。

    2.2 三个通道在 PC 上的用途

    通道I/O 数据端口PC/AT 典型用途本章
    0 0x40 系统定时器 → PIC IRQ0 被 IMR 屏蔽
    1 0x41 历史:DRAM 刷新(现代几乎不用) 未使用
    2 0x42 PC 扬声器(与 0x61 bit0 Gate 配合) 未使用

    #mermaid-svg-4p7RKrlYiVXeY5Pk{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4p7RKrlYiVXeY5Pk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4p7RKrlYiVXeY5Pk .error-icon{fill:#552222;}#mermaid-svg-4p7RKrlYiVXeY5Pk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4p7RKrlYiVXeY5Pk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4p7RKrlYiVXeY5Pk .marker.cross{stroke:#333333;}#mermaid-svg-4p7RKrlYiVXeY5Pk svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4p7RKrlYiVXeY5Pk p{margin:0;}#mermaid-svg-4p7RKrlYiVXeY5Pk .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-4p7RKrlYiVXeY5Pk .cluster-label text{fill:#333;}#mermaid-svg-4p7RKrlYiVXeY5Pk .cluster-label span{color:#333;}#mermaid-svg-4p7RKrlYiVXeY5Pk .cluster-label span p{background-color:transparent;}#mermaid-svg-4p7RKrlYiVXeY5Pk .label text,#mermaid-svg-4p7RKrlYiVXeY5Pk span{fill:#333;color:#333;}#mermaid-svg-4p7RKrlYiVXeY5Pk .node rect,#mermaid-svg-4p7RKrlYiVXeY5Pk .node circle,#mermaid-svg-4p7RKrlYiVXeY5Pk .node ellipse,#mermaid-svg-4p7RKrlYiVXeY5Pk .node polygon,#mermaid-svg-4p7RKrlYiVXeY5Pk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4p7RKrlYiVXeY5Pk .rough-node .label text,#mermaid-svg-4p7RKrlYiVXeY5Pk .node .label text,#mermaid-svg-4p7RKrlYiVXeY5Pk .image-shape .label,#mermaid-svg-4p7RKrlYiVXeY5Pk .icon-shape .label{text-anchor:middle;}#mermaid-svg-4p7RKrlYiVXeY5Pk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4p7RKrlYiVXeY5Pk .rough-node .label,#mermaid-svg-4p7RKrlYiVXeY5Pk .node .label,#mermaid-svg-4p7RKrlYiVXeY5Pk .image-shape .label,#mermaid-svg-4p7RKrlYiVXeY5Pk .icon-shape .label{text-align:center;}#mermaid-svg-4p7RKrlYiVXeY5Pk .node.clickable{cursor:pointer;}#mermaid-svg-4p7RKrlYiVXeY5Pk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4p7RKrlYiVXeY5Pk .arrowheadPath{fill:#333333;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4p7RKrlYiVXeY5Pk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4p7RKrlYiVXeY5Pk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4p7RKrlYiVXeY5Pk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4p7RKrlYiVXeY5Pk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4p7RKrlYiVXeY5Pk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4p7RKrlYiVXeY5Pk .cluster text{fill:#333;}#mermaid-svg-4p7RKrlYiVXeY5Pk .cluster span{color:#333;}#mermaid-svg-4p7RKrlYiVXeY5Pk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4p7RKrlYiVXeY5Pk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4p7RKrlYiVXeY5Pk rect.text{fill:none;stroke-width:0;}#mermaid-svg-4p7RKrlYiVXeY5Pk .icon-shape,#mermaid-svg-4p7RKrlYiVXeY5Pk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4p7RKrlYiVXeY5Pk .icon-shape p,#mermaid-svg-4p7RKrlYiVXeY5Pk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4p7RKrlYiVXeY5Pk .icon-shape .label rect,#mermaid-svg-4p7RKrlYiVXeY5Pk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4p7RKrlYiVXeY5Pk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4p7RKrlYiVXeY5Pk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4p7RKrlYiVXeY5Pk :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    8253 三通道

    OUT

    OUT

    OUT

    Channel 0 系统时钟

    Channel 1 刷新

    Channel 2 扬声器

    8259A IRQ0

    内存刷新电路

    0x61 bit0 Gate

    扬声器


    3. I/O 端口

    端口方向含义
    0x40 读/写 Channel 0 计数寄存器
    0x41 读/写 Channel 1 计数寄存器
    0x42 读/写 Channel 2 计数寄存器
    0x43 控制字(选择通道、模式、读/写方式)

    注意: 三个数据端口 共用 一套「先写控制字、再写分频值」的协议;必须先向 0x43 写入 控制字,再向 0x40/0x41/0x42 写入 16 位分频值(低字节、高字节顺序由控制字决定)。


    4. 控制字(Control Word)格式

    向 0x43 写入 1 字节 控制字,格式如下:

    7 6 5 4 3 2 1 0
    ┌───┬───┬───────┬───────────────┐
    │ SC│ RW│ MODE │ BCD │
    └───┴───┴───────┴───────────────┘

    字段位含义
    SC 7~6 选择通道:00=Ch0,01=Ch1,10=Ch2,11=读回命令
    RW 5~4 读/写方式:00=锁存,01=仅低字节,10=仅高字节,11=先低后高
    MODE 3~1 工作模式 0~5
    BCD 0 0=二进制计数,1=BCD(PC 上恒用 0)

    示例: 配置 Channel 0、先写低字节再写高字节、Mode 3、二进制:

    SC=00

    ,

     RW=11

    ,

     MODE=011

    ,

     BCD=0

    \\text{SC=00},\\ \\text{RW=11},\\ \\text{MODE=011},\\ \\text{BCD=0}

    SC=00, RW=11, MODE=011, BCD=0

    0

    x

    0011011

    2

    =

    0

    x

    36

    \\Rightarrow 0x0011011_2 = \\mathbf{0x36}

    0x00110112=0x36


    5. 六种工作模式(MODE 0~5)

    模式名称典型用途
    0 中断结束计数 一次性定时,到时输出高电平
    1 硬件可重触发单稳 脉冲宽度由计数值决定
    2 速率发生器 周期性分频,计数到 1 时输出一个周期低脉冲
    3 方波发生器 Channel 0 默认:对称方波,占空比约 50%
    4 软件触发选通 写入计数值后启动
    5 硬件触发选通 外部 Gate 触发

    Channel 0 系统时钟 几乎总是 Mode 3(方波):输出在 0/1 间周期性翻转,每个周期可在 OUT 下降沿 触发 IRQ0(具体边沿与模式有关;BIOS 默认配置下约 18.2 Hz 进 IRQ0)。

    Mode 2(速率发生器)也常见于 OS 自行初始化 PIT 时(如 Linux 早期 tick)。


    6. 初始化 Channel 0 示例(下一章参考)

    下列代码 本章未使用;演示如何把 PIT 设为 约 100 Hz,并配合 PIC/IDT 开放 IRQ0:

    ; 1193182 / 100 ≈ 11932
    PIT_HZ equ 100
    PIT_DIV equ 1193182 / PIT_HZ

    init_pit_ch0:
    ; 控制字:Ch0, 先低后高, Mode 3, 二进制 → 0x36
    MOV AL, 0x36
    OUT 0x43, AL

    MOV AX, PIT_DIV
    OUT 0x40, AL ; 低 8 位
    MOV AL, AH
    OUT 0x40, AL ; 高 8 位
    RET

    配套步骤(下一章):

  • IDT 注册 向量 32(IRQ0_VECTOR equ 32)
  • init_pit_ch0 设置分频
  • PIC IMR 清 bit0,开放 IRQ0:IN 0x21 → AND AL, 0xFE → OUT 0x21, AL
  • 定时器 ISR 内 tick++,发 EOI OUT 0x20, 0x20

  • 7. 常用分频对照表

    分频值 N频率(约)周期典型场景
    65535 18.2 Hz ~55 ms BIOS 默认 tick
    11932 100 Hz 10 ms 简易内核 heartbeat
    1193 1000 Hz 1 ms 较细粒度计时(开销大)
    1193182 1 Hz 1 s 慢速演示

    公式:N = 1193182 / 目标 Hz(整数除,会有微小误差)。


    8. Channel 2 与 PC 扬声器(了解)

    Channel 2 输出接 8255 PPI 端口 0x61 bit0(Gate),Gate=1 时方波驱动扬声器:

    ; 设置 Ch2 频率(控制字 0xB6 = Ch2, 先低后高, Mode 3)
    MOV AL, 0xB6
    OUT 0x43, AL
    ; 写入分频值到 0x42 …
    ; 打开 Gate
    IN AL, 0x61
    OR AL, 3 ; bit0 Gate, bit1 数据(简化)
    OUT 0x61, AL

    本章节未使用,与键盘中断无直接关系。


    9. 一次 IRQ0 定时中断的流程

    timer_handler

    CPU

    8259A IRQ0

    8253 Ch0

    1.193182 MHz

    timer_handler

    CPU

    8259A IRQ0

    8253 Ch0

    1.193182 MHz

    #mermaid-svg-momyx1WMmlMTxbIN{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-momyx1WMmlMTxbIN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-momyx1WMmlMTxbIN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-momyx1WMmlMTxbIN .error-icon{fill:#552222;}#mermaid-svg-momyx1WMmlMTxbIN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-momyx1WMmlMTxbIN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-momyx1WMmlMTxbIN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-momyx1WMmlMTxbIN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-momyx1WMmlMTxbIN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-momyx1WMmlMTxbIN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-momyx1WMmlMTxbIN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-momyx1WMmlMTxbIN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-momyx1WMmlMTxbIN .marker.cross{stroke:#333333;}#mermaid-svg-momyx1WMmlMTxbIN svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-momyx1WMmlMTxbIN p{margin:0;}#mermaid-svg-momyx1WMmlMTxbIN .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-momyx1WMmlMTxbIN text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-momyx1WMmlMTxbIN .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-momyx1WMmlMTxbIN .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-momyx1WMmlMTxbIN .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-momyx1WMmlMTxbIN .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-momyx1WMmlMTxbIN #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-momyx1WMmlMTxbIN .sequenceNumber{fill:white;}#mermaid-svg-momyx1WMmlMTxbIN #sequencenumber{fill:#333;}#mermaid-svg-momyx1WMmlMTxbIN #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-momyx1WMmlMTxbIN .messageText{fill:#333;stroke:none;}#mermaid-svg-momyx1WMmlMTxbIN .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-momyx1WMmlMTxbIN .labelText,#mermaid-svg-momyx1WMmlMTxbIN .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-momyx1WMmlMTxbIN .loopText,#mermaid-svg-momyx1WMmlMTxbIN .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-momyx1WMmlMTxbIN .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-momyx1WMmlMTxbIN .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-momyx1WMmlMTxbIN .noteText,#mermaid-svg-momyx1WMmlMTxbIN .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-momyx1WMmlMTxbIN .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-momyx1WMmlMTxbIN .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-momyx1WMmlMTxbIN .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-momyx1WMmlMTxbIN .actorPopupMenu{position:absolute;}#mermaid-svg-momyx1WMmlMTxbIN .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-momyx1WMmlMTxbIN .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-momyx1WMmlMTxbIN .actor-man circle,#mermaid-svg-momyx1WMmlMTxbIN line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-momyx1WMmlMTxbIN :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    每个时钟递减计数

    计数到 0,OUT 翻转

    IRQ0 有效

    INTR(若 IMR bit0=0)

    INTA

    向量 32

    IDT[32]

    tick++, 任务调度等

    OUT 0x20, 0x20 EOI

    IRET

    与 IRQ1 键盘 对比:

    对比项IRQ0(8253)IRQ1(8042 键盘)
    设备 PIT Channel 0 8042 键盘接口
    数据端口 0x40~0x43 0x60/0x64
    触发频率 周期性(如 18.2 Hz) 事件驱动(按键)
    重映射后向量 32 33
    本章 IMR 屏蔽 开放并处理
    ISR 典型工作 tick++、调度 读扫描码入缓冲

    10. 本章与 8253 的关系

    项目本章做法原因
    PIT 初始化 不改(沿用 BIOS 默认 Ch0) 只演示键盘中断
    PIC IRQ0 屏蔽(IMR bit0=1) 无 IDT[32],避免 triple fault
    PIT 端口 不访问 避免破坏 BIOS 已设频率

    BIOS 启动后 Channel 0 通常已在运行并 不断请求 IRQ0;本章用 0xFD 屏蔽 IRQ0 是刻意为之。下一章实现 定时器中断 时,再按 第 6 节 初始化 PIT、注册 IDT[32]、开放 IMR bit0 即可。


    五、8042 与键盘中断使能

    与 lesson13 相同:0xAE 启用键盘 + 0xF4 开启扫描。不在 Bochs 里读写 8042 命令字节(易卡死或破坏键盘)。

    注意: 不要使用 0xAD/0xA7/0xD2 那套「完整 OSDev 流程」——在 Bochs 里可能 卡死在 wait_8042。

    init_8042:
    ; 0xAE → drain → 0xF4(kbd_cmd)→ drain
    RET

    初始化顺序: CLI → init_pic(仅屏蔽 IRQ0)→ init_idt(向量 9 与 33)→ init_8042 → STI。


    六、IRQ1 处理程序与环形缓冲

    1. 为何用缓冲

    中断处理应尽量 短:读硬件、存数据、EOI,复杂逻辑放到 read_char(主线程上下文)。扫描码先写入 环形队列,避免中断里调用 print_char 等可能重入的代码。

    KBD_RING_SIZE equ 64
    kbd_ring: times KBD_RING_SIZE db 0
    kbd_rpos: dd 0
    kbd_wpos: dd 0

    2. irq1_handler

    irq1_handler:
    PUSH EAX

    IN AL, 0x60
    CALL kbd_ring_push

    MOV AL, 0x20
    OUT 0x20, AL ; EOI

    POP EAX
    IRET

    注意: ISR 必须用 IRET 返回;此处只保存/恢复 EAX 即可。

    3. read_char:缓冲优先,端口兜底

    kbd_pop_scancode:
    MOV EAX, [kbd_rpos]
    CMP EAX, [kbd_wpos]
    JNE .from_ring
    CALL read_kbd_scancode ; 缓冲空:与 lesson13 相同,读 8042
    RET
    .from_ring:
    ; 从 kbd_ring 取一字节
    RET

    设计说明:

    • IRQ 正常时:irq1_handler 先入队,kbd_pop 只消费缓冲,不读端口。
    • Bochs 实测:IDT/PIC/IF 均正确时 IRQ1 仍可能进不了 handler,但 8042 OBF 有数据;缓冲空时 回退到 read_kbd_scancode,与 lesson13 一致,保证能打字。
    • 仍是 lesson14 架构(IDT + PIC + ISR + 环形缓冲),不是退回纯 lesson13。

    read_char 仍采用:等按下 → 等释放 → 查 scancode_map。


    七、kernel.c

    与 lesson13 相同,无需感知中断细节:

    void main(void)
    {
    clear_screen();
    init_keyboard(); /* 内部:PIC + IDT + 8042 + STI */
    print_str("NotOneOS 32-bit Protected Mode.\\r\\nWelcome..\\r\\n", ...);
    print_str("> ", 2);

    char a;
    fin:
    a = read_char();
    if (a != 0) {
    print_char(a);
    }
    goto fin;
    }


    八、编译与运行

    cd lesson14
    make
    make install
    make run # Bochs
    make debug # 调试

    验证清单:

    • 欢迎语与 > 提示符正常
    • 软件光标 _ 可见
    • 点击 Bochs 窗口 获得焦点后,按键可回显
    • 调试器中 IF=1,read_char 停在 HLT 而非 IN 0x64 轮询
    • 无 triple fault

    调试建议(详见下方「按键无反应排查」):

    make debug
    b 0xC200
    c

    地址以 ../target/kernel.map 为准(make 后自动生成)。


    按键无反应排查

    0. 你的日志说明什么

    若 tools/bochsout.txt 末尾类似:

    IF=1
    EIP=0000cc5f
    >> call … (0x0000cc33)

    表示:欢迎语已打印,STI 已执行,CPU 在 read_char → kbd_pop → HLT 里等键盘中断,不是 lesson13 那种 IN 0x64 轮询。按键无反应 = IRQ1 没进来或 8042 没把扫描码送给 PIC。

    1. 先排除低级问题

    • 重新 make install 再运行。
    • 鼠标点击 Bochs 显示窗口(黑屏区域),不是下方调试命令行。
    • 确认能看到 Welcome.. 和 > 提示符。

    2. 用断点定位「中断有没有触发」

    重要: CPU 停在断点上时按键不会进中断。须 bc 清断点或只保留 irq1_handler,再 c 继续运行后按键。

    make 后打开 ../target/kernel.map(当前典型地址):

    符号典型地址作用
    irq1_handler 0xCC2F 按键应最先进这里
    read_char 0xCC5D 主循环读键
    init_keyboard 0xCBF6 末尾应已 STI
    IDT[9] 0xC342 默认 PIC_REMAP=0 时查此项

    推荐命令:

    bc
    b 0xCC2F
    c

    点击 Display 窗口,按 a(调试器命令行不要抢焦点):

    • 能断在 irq1_handler → 中断通,查 scancode_map / 环形缓冲。
    • 断不到 → 继续第 4、5 步。

    验证 IDT(PIC_REMAP=0 时查向量 9):

    xp /8bx 0xC342
    info cpu

    期望:06 2f cc 08 00 8e 00 00 一类(指向 0xCC2F),且 eflags 含 IF。

    3. 对照 lesson13(最快二分)

    用 lesson13 编译运行:若 lesson13 能回显、lesson14 不能 → 8042 有数据,问题在 IDT/PIC/8042 命令字节 bit0。若 两个都不行 → 先查 Bochs 焦点和键盘配置。

    4. 查 PIC 与向量号(最常见根因)

    代码里 utilities.s 顶部:

    PIC_REMAP equ 0 ; Bochs 默认:不重映射,IRQ1 → 向量 9
    ; 改为 1 则 ICW 重映射,IRQ1 → 向量 33

    PIC_REMAPIRQ1 向量IDT 应注册主片 IMR
    0(Bochs 推荐) 9 IDT[9] @ 0xC342 读改写:屏蔽 IRQ0、开放 IRQ1
    1(真 OS 常用) 33 IDT[33] @ 0xC402 写 0xFD

    你之前的症状(IDT[33] 正确、IF=1、按 a 不进 handler):多半是 Bochs 仍按 BIOS 发向量 9,或 8042 命令字节 bit0 未真正置 1。当前默认已改为 PIC_REMAP=0 + 读命令字节前 drain_kbd。

    • 按键后 立刻重启 → IDT 向量与 PIC 映射不一致(例如 remap=1 却只注册了 9)。
    • 不重启也不进 handler → 8042 未开 IRQ(查命令字节 bit0)或未点 Display 焦点。

    5. 查 8042 是否有数据(中断以外的证据)

    程序停在 HLT 时,若能在调试器里看到 0x64 的 bit0(OBF)随按键变 1,说明扫描码已到 8042,只是 没产生 IRQ → 重点查 init_8042 里命令字节 bit0 是否为 1。

    6. 现象对照

    现象方向
    IF=1,EIP 在 kbd_pop/HLT 等中断;查 8042 bit0、PIC IMR、窗口焦点
    按键即重启 IDT 向量号与 PIC 映射不一致
    进了 irq1_handler 无字符 查表 / make-break 逻辑
    lesson13 行、lesson14 不行 中断路径问题

    九、常见问题

    现象可能原因
    triple fault 启动即崩溃 IDT 未注册却 STI;或 IRQ0(8253) 未屏蔽且无 IDT[32]
    有欢迎语但按键无反应 未点击 Bochs 窗口;8042 未开扫描/未开中断位
    中断进错向量 PIC 未重映射却用向量 9;或重映射了却用向量 9
    只响应一次键 忘记 EOI
    乱码 scancode_map 偏移错误(见 lesson13 说明)

    向量号对照:

    配置IRQ1 对应向量
    BIOS 默认(不重映射 PIC) 9
    本章 PIC 重映射到 0x20 33

    本章采用 重映射 + 向量 33,与主流教程(《30天自制操作系统》等)一致。


    十、PIC_REMAP 必须为 1:踩坑与修复

    理论背景(向量冲突、Triple Fault、ICW 四步握手)已写入 第三节 §3.1。本节只保留实践结论。

    1. 问题现象

    若 PIC_REMAP equ 0(沿用 BIOS 默认 IRQ0~7 → 向量 8~15),在 Bochs 中常见:按键无反应,且很快自动重启(triple fault)。

    2. 根因(一句话)

    BIOS 映射与 CPU 异常区 0~31 重叠;PIT 的 IRQ0 以向量 8(#DF)进来,空 IDT 门 → #GP → #DF → Triple Fault。即使勉强屏蔽 IRQ0,不写完整 ICW 也无法可靠开放 IRQ1、配置从片与 8086 模式。

    3. 修复

    将 utilities.s 中改为:

    PIC_REMAP equ 1 ; 必须为 1:保护模式下必须重映射 8259A

    init_pic 走完整 ICW1~4(主片基址 0x20、从片 0x28)+ IMR 0xFD/0xFF,IRQ1 → 向量 33,与 IDT[33] 一致。对照表见 §3.1 与 §6。

    4. 教训总结

    要点说明
    保护模式必须重映射 PIC BIOS 默认 8~15 与异常重叠,IRQ0 一触发即 triple fault
    IMR 必须显式设置 写 0xFD 精确只开放 IRQ1,不能赌 BIOS 遗留值
    IDT 向量号必须与 PIC 映射一致 重映射后 IRQ1→33;不一致则按键重启或无反应
    ICW4 的 8086 模式位不可省略 不写 ICW4 则 EOI / 向量格式可能与 x86 不兼容

    十一、本章小结

    • IDT 定义「向量号 → 处理函数」;8259A PIC 定义「IRQ 线 → 向量号」,并通过 IMR 屏蔽 / EOI 结束 管理中断生命周期
    • 8253 PIT 以 1.193182 MHz 分频驱动 IRQ0;BIOS 默认约 18.2 Hz;本章 屏蔽 IRQ0,下一章再初始化 Channel 0 并注册 IDT[32]
    • 8259A 主从级联共 15 路 IRQ;键盘占 主片 IRQ1,重映射后对应 向量 33
    • 键盘中断链路:按键 → 8042 → PIC IRQ1 → CPU → IDT[33] → irq1_handler → EOI
    • 环形缓冲 + HLT 把「硬件响应」与「字符处理」解耦,CPU 不再空转
    • lesson13 的显存输出、软件光标、扫描码表可原样复用

    后面章节可在此基础上增加:8253 定时器 IRQ0(tick / sleep)、Shift 大写、或 分页(CR3 + 页表)。

    源码: https://gitee.com/xundh/learn-os/tree/master/lesson14

    赞(0)
    未经允许不得转载:171主机测评 » 操作系统学习14 IDT + PIC + 键盘中断
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址