2. 实现串口接收中断

任务目标

在BIOS开发环境中,配置串口(COM1,I/O端口0x3F8)的接收中断,编写中断服务程序(ISR),实现从串口接收单个字符并将其回显(发送回同一串口)。本案例聚焦于8259A中断控制器与16550 UART的初始化及中断处理流程。

环境与依赖

  • 处理器:x86(实模式)
  • 中断控制器:8259A(主片基地址0x20)
  • 串口芯片:16550 UART(COM1基地址0x3F8)
  • 中断向量号:IRQ4对应中断向量0x0C(主片第4个中断请求)
  • 汇编器:NASM(或兼容x86汇编器)
  • 运行环境:模拟器(如QEMU)或真实硬件

实现原理

串口接收中断的完整链路为:UART收到字符后,其接收缓冲寄存器(RBR)有数据,触发中断请求;UART的中断输出引脚连接到8259A主片的IRQ4;8259A将IRQ4映射为中断向量0x0C并通知CPU;CPU响应中断后跳转到我们注册的ISR。ISR中需要读取RBR以清除中断状态,然后将该字符写入发送保持寄存器(THR)完成回显,最后向8259A发送EOI(End of Interrupt)命令。

UART (COM1) 0x3F8 基地址 收到字符→RBR 8259A 主片 IRQ4 输入 映射到向量 0x0C CPU 响应中断 执行 ISR ISR 读RBR→写THR 发送EOI 回显字符(写THR)

完整代码

以下代码使用NASM语法,包含完整的初始化、ISR实现和主循环。代码可在实模式下直接运行,通过QEMU的-serial参数进行验证。

;--------------------------------------------------------
; 串口接收中断实现 - COM1 (0x3F8), IRQ4 → 向量 0x0C
; 功能:接收字符并回显
; 汇编:nasm -f bin uart_int.asm -o uart_int.bin
;--------------------------------------------------------
[org 0x7C00]
[bits 16]

; 初始化段寄存器
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00

; 保存原中断向量
mov ax, [0x0C * 4]
mov [old_int_off], ax
mov ax, [0x0C * 4 + 2]
mov [old_int_seg], ax

; 设置新中断向量
mov word [0x0C * 4], uart_isr
mov word [0x0C * 4 + 2], cs

; 初始化8259A(主片)
mov al, 0x11          ; ICW1: 边沿触发,级联,需要ICW4
out 0x20, al
mov al, 0x08          ; ICW2: 中断向量基地址 0x08
out 0x21, al
mov al, 0x04          ; ICW3: 从片连接在IR2
out 0x21, al
mov al, 0x01          ; ICW4: 8086模式
out 0x21, al
mov al, 0xEF          ; OCW1: 只屏蔽IRQ4(清除bit4)
out 0x21, al

; 初始化UART (COM1)
mov dx, 0x3FB         ; LCR (Line Control Register)
mov al, 0x80          ; DLAB = 1
out dx, al
mov dx, 0x3F8         ; DLL (Divisor Latch Low)
mov al, 0x0C          ; 波特率 9600 (115200/9600=12)
out dx, al
mov dx, 0x3F9         ; DLM (Divisor Latch High)
mov al, 0x00
out dx, al
mov dx, 0x3FB         ; LCR
mov al, 0x03          ; 8位数据,无校验,1停止位,DLAB=0
out dx, al
mov dx, 0x3F9         ; IER (Interrupt Enable Register)
mov al, 0x01          ; 使能接收数据可用中断
out dx, al
mov dx, 0x3FC         ; MCR (Modem Control Register)
mov al, 0x0B          ; 启用OUT2(使中断输出到8259A)
out dx, al

sti                   ; 开中断

; 主循环
main_loop:
    hlt               ; 等待中断
    jmp main_loop

;--------------------------------------------------------
; 串口接收中断服务程序
;--------------------------------------------------------
uart_isr:
    push ax
    push dx

    mov dx, 0x3F8     ; RBR (Receiver Buffer Register)
    in al, dx         ; 读取接收字符(同时清除中断状态)
    mov dx, 0x3F8     ; THR (Transmitter Holding Register)
    out dx, al        ; 回显字符

    mov al, 0x20      ; EOI命令
    out 0x20, al      ; 发送到8259A主片

    pop dx
    pop ax
    iret

;--------------------------------------------------------
; 数据区
;--------------------------------------------------------
old_int_off: dw 0
old_int_seg: dw 0

times 510-($-$$) db 0
dw 0xAA55             ; 引导扇区标志

运行与验证

  1. 编译:nasm -f bin uart_int.asm -o uart_int.bin
  2. 运行(QEMU):qemu-system-i386 -fda uart_int.bin -serial stdio
  3. 验证步骤:
    • 在QEMU终端中直接输入字符(如字母A),该字符会通过串口发送到虚拟机。
    • UART收到字符后触发IRQ4中断,ISR读取该字符并写回THR。
    • 回显的字符会出现在QEMU终端中。若输入A后终端显示A,则功能正常。
  4. 通过条件:每个输入的字符都能立即在终端上显示出来,且系统不崩溃、不进入死循环。

关键说明

  • MCR的OUT2位(bit3)必须置1,否则UART的中断信号无法到达8259A,这是初学者最容易遗漏的步骤。
  • 读取RBR会自动清除接收中断状态;若不清除,中断会反复触发。
  • 8259A初始化后,OCW1写入0xEF表示只开放IRQ4(bit4=0),其他IRQ保持屏蔽。
  • 本代码未处理串口发送中断和线路状态中断,仅聚焦于接收中断的最小闭环。