外中断
接口芯片和端口
外设(键盘、鼠标、串口等)通过接口芯片与 CPU 连接。接口芯片有两类端口:
- 控制端口:CPU 写入,告诉设备做什么
- 状态端口:CPU 读取,了解设备状态
- 数据端口:传输实际数据
当外设输入数据后,相关接口芯片会向 CPU 发出中断信息,这就是外中断。
可屏蔽中断与不可屏蔽中断
| 类型 | 引脚 | 说明 |
|---|---|---|
| 可屏蔽中断(INTR) | INTR 引脚 | IF=1 时才响应;可以被 cli 屏蔽 |
| 不可屏蔽中断(NMI) | NMI 引脚 | CPU 必须立即响应,不可屏蔽 |
控制可屏蔽中断:
asm
sti ; IF=1,开中断(允许响应可屏蔽中断)
cli ; IF=0,关中断(屏蔽可屏蔽中断)在编写中断例程时,通常需要
cli保护临界区,完成后sti恢复。
键盘输入与 int 9
扫描码与键盘缓冲区
- 按下/松开某键 → 键盘产生扫描码
- 扫描码通过
60h端口进入 CPU - CPU 响应 9 号中断,执行 BIOS int 9 中断例程
- int 9 读取 60h 端口扫描码,转化为 ASCII 码,存入键盘缓冲区
键盘缓冲区是一个环形队列,容量 15 个字(每个字 = 扫描码 + ASCII 码)。
BIOS int 16h:读取键盘缓冲区
asm
mov ah, 0
int 16h ; 等待并读取键盘输入
; 返回:ah = 扫描码,al = ASCII 码编写自定义 int 9 例程
目标: 按下 ESC 键时不退出,改变屏幕字符颜色;按其他键正常显示。
思路:
- 保存原 int 9 中断向量
- 安装新例程,新例程先调用原例程(处理正常键盘逻辑),再自定义处理
- 程序退出前恢复原中断向量
asm
assume cs:code
stack segment
db 128 dup (0)
stack ends
data segment
dw 0, 0 ; 存原 int 9 的 IP 和 CS
data ends
code segment
start:
; 初始化栈
mov ax, stack
mov ss, ax
mov sp, 128
; 保存原 int 9 向量
mov ax, 0
mov es, ax
mov ax, data
mov ds, ax
mov ax, es:[9*4] ; 原 IP
mov ds:[0], ax
mov ax, es:[9*4+2] ; 原 CS
mov ds:[2], ax
; 安装新 int 9
cli
mov word ptr es:[9*4], offset int9
mov word ptr es:[9*4+2], cs
sti
; 主程序:显示 a~z,按 ESC 退出,其他键改变颜色
mov ax, 0b800h
mov es, ax
mov ah, 7 ; 初始颜色属性(白色)
mov bx, 0
showloop:
mov al, 'a'
mov es:[bx], al ; 显示字符
mov es:[bx+1], ah ; 颜色
add bx, 2
cmp bx, 160*25
jb showloop
mov bx, 0 ; 等待按键(通过 int 16h 阻塞)
wait:
mov ah, 0
int 16h
cmp al, 1bh ; ESC 的 ASCII 码
je quit
inc ah ; 改变颜色
mov bx, 0
; 重新用新颜色刷新屏幕
refresh:
mov es:[bx+1], ah
add bx, 2
cmp bx, 160*25
jb refresh
jmp wait
quit:
; 恢复原 int 9
mov ax, 0
mov es, ax
mov ax, data
mov ds, ax
cli
mov ax, ds:[0]
mov es:[9*4], ax
mov ax, ds:[2]
mov es:[9*4+2], ax
sti
mov ax, 4c00h
int 21h
; 新 int 9 中断例程
int9:
push ax
push bx
push ds
; 先调用原 int 9 处理键盘输入
pushf
call dword ptr ds:[0] ; 调用原例程(模拟 int 调用)
; 读取刚输入的扫描码(已被原例程从端口读走,查缓冲区)
; (此处简化,原版需从 60h 端口读,此为示意)
pop ds
pop bx
pop ax
iret
code ends
end start小结:外中断处理流程
外设动作
→ 接口芯片向 CPU 发 INTR 信号
→ CPU 在执行完当前指令后检测 INTR
→ 若 IF=1,执行中断过程(pushf, cli, push CS:IP, 读向量表)
→ 跳到中断例程执行
→ iret 返回与内中断的区别:外中断来自外设,受 IF 标志控制(可屏蔽);内中断由 CPU 内部逻辑触发,不受 IF 影响(除了单步中断 type 1)。