8086CPU的中断处理笔记

《汇编语言》第三版有关中断处理的学习笔记。

相关概念

  • 什么是中断?

    一种来自CPU内部或者外部的中断信息,被CPU检测到,CPU在执行完当前正在执行的指令后,可以立即对所接收到的信息进行处理。

    Wiki上的解释更为简单点:

    In digital computers, an interrupt is an input signal to the processor indicating an event that needs immediate attention.

  • 中断可分为哪些类型?

    按照是否发生在CPU内部,分为:

    • 内中断:发生在CPU内部的中断, 比如溢出。

    • 外中断:发生在CPU外部的中断,比如外部设备输入

  • 中断处理的基本机制是什么?

    一般分四步:

    • 获取中断类型码 N
    • 标志寄存器入栈,TF,IF 设置为0
    • CS,IP入栈
    • (IP)= (N * 4), (CS) = (N * 4 + 2)

    其中,外中断与内中断在第一步,即获取中断类型码N稍有不同, 因为外中断的中断信息来自CPU外部,中断类型码通过数据总线送入CPU的, 而内中断的中断类型码是直接在CPU内部产生的。

    中断后面的三步可转换成如下汇编语言:

    ; 标志寄存器入栈
    pushf
    
    ; 设置 TF,IF 为0,TF,IF为标志寄存器的8,9位
    pushf
    pop bx
    and bh, 11111100b
    push bx
    popf
    
    ; CS,IP入栈
    push CS
    push IP
    

    这里解释下为什么要设置 TF=0, IF=0。

    • TF 与单步中断有关

      单步中断的类型码为1, CPU检测TF,如果 TF =1,则产生单步中断,CPU会去执行1号中断处理程序,故这里将TF设置为0。

    • IF与外中断相关

      CPU可以不响应的外中断称为 可屏蔽中断。CPU通过IF的值决定是否响应可屏蔽中断。

      当 IF=1,CPU执行完当前指令后,响应中断,引发中断过程。IF=0,CPU不响应可屏蔽中断。

      所以这里设置IF=0,这样,在进入中断例程后,便禁止了其他的可屏蔽中断。

  • 如何根据中断类型码找到对应的中断例程的入口地址?

    通过中断向量表 IDT Interrupt description table。

    对8086机,中断向量表指定存放在内存地址0处,共有256种中断类型,一个表项存放一个中断例程的入口地址,即CS:IP,故整个中断向量表占用 4 byte * 256 = 1024 byte,即内存地址从0000:0000 到 0000:03FF的1024个单元中存放着中断向量表。

  • 中断例程有哪些?

    中断例程分两种:

    • 程序员自己编写的

    • 系统自带的:可由 BIOS 或 DOS提供。

      BIOS:基本输入输出系统,存放在ROM中。

      BIOS 中主要包括的中断:

      • 外部中断和内部中断的中断例程
      • 用于对硬件设备进行 I/O操作的中断例程 「比如在屏幕进行打印,int 10h」
      • 其他和硬件系统相关的中断例程

      操作系统DOS 也提供了中断处理,注意,和硬件设备相关的DOS 中断例程中,一般都调用了BIOS中的中断例程。

      BIOS, DOS 提供的中断例程,都是使用ah 来传递子程序的编号。

      比如(ah)=9, 表示调用第 N 号中断例程的9号子程序。

      8086支持的中断例程列表:bios and dos interrupts

  • 程序员如何自己编写中断处理程序?

    三步走:

    • 编写中断处理程序

    • 将中断处理程序复制到指定的内存地址中

    • 设置中断向量表

    这里,将中断处理程序复制到内存中哪个地方合适?

    内存地址从0000:0000 到 0000:03FF的1024个单元中,存放着256种中断类型的入口地址,但实际上,系统要处理的中断事件远没有达到256个,一般情况下,从0000:0200 到 0000:02FF 的256个字节所对应的中断向量表项都是空的,可以安放在此处。

  • 有哪些常见的中断例程?

    int 10h:

    BIOS 提供, (ah)=2h, 表示调用第 10h 号中断例程的2号子程序,置光标, (ah)=9h, 表示调用第 10h 号中断例程的9号子程序,在光标位置显示字符。

    int 21h:

    DOS 提供, (ah)=4ch, 表示调用第 21h 号中断例程的4c号子程序,即退出程序, (ah)=9h, 表示调用第 21h 号中断例程的9号子程序,在光标位置显示字符串。

    int 9h:

    BIOS 提供。从60h端口读出输入的扫描码,并将其转化为相应的ASCII码或状态信息,存储在内存的指定空间(键盘缓冲区/状态字节)中。

    Int 16h

    BIOS提供,该中断例程包含的0号子程序,可以从键盘缓冲区中读取一个键盘的输入。

端口读写

外中断的处理,涉及到端口读写,来了解下。

CPU 可以直接读写3个地方的数据:

  • CPU内部的寄存器
  • 内存单元
  • 端口

CPU 对端口的读写只有两个指令: in (读取), out(写入).

读写端口,只能通过 ax / al, 即读入的信息和写入的信息都通过 ax / al 传递。

端口地址范围:0~65535

  • 0~255的端口读写:

    in al, 20h
    out 20h, al
    
  • 256~65535的端口读写:

    mov dx, 3ffh ;需要一个16位寄存器来存放端口地址
    in al, dx
    out dx, al
    

有关外中断

主要为处理外部设备的输入输出,比如键盘的输入。

外设与CPU交互通过端口实现:

外设的输入先送入相关的接口芯片的端口,CPU通过访问端口来读取外设的输入。

同样,CPU向外设的输出也不是直接送入外设,而是先送入端口,再由相关芯片送入外设,即端口相当于一个中间站。CPU通过端口与外设联系。

外中断分为两类:

  • 可屏蔽中断: CPU 可以不响应的外中断

    通过 标志寄存器中的 IF位来判断是否屏蔽。

    8086CPU提供了设置IF的指令:

    sti ,设置 IF=1

    cli,设置 IF=0

    几乎所有由外设引发的外中断,都是可屏蔽中断。

  • 不可屏蔽中断:CPU必须响应的外中断

    对于8086CPU, 不可屏蔽中断的中断类型码固定为2。

    不可屏蔽中断是系统中,有必须处理的紧急情况发生时,用来通知CPU的中断信息。

以键盘输入引发的中断为例:

松开或者按下键盘时,会产生一个扫描码,该扫描码会被送入60h 端口,引发9号中断,CPU检测到该中断后,如果IF=1,则响应中断,引发中断过程,去执行int 9h 中断例程。

即整个流程为:

  • 键盘产生扫描码
  • 扫描码送至60h端口
  • 引发9号中断
  • CPU执行BIOS提供的 int 9h 中断例程

参考

《汇编语言》(第3版)

x86 instruction listings

bios and dos interrupts