汇编语言学习笔记(五)

0x00 内中断

任何一个通用的CPU都可以在执行完当前正在执行的指令之后,检测到从CPU外部发送过来的或内部产生的一种特殊信息,并且可以立即对所接收到的信息进行处理。这种特殊的信息称为中断信息。中断是指CPU不再接着向下执行,而是转去处理这个特殊信息。

中断信息来自内部和外部,本节主要讨论内部中断。

内中断的产生

8086CPU使用称为中断类型码的数据来标识中断信息的来源,中断类型码为一个字节型数据,可以表示256种中断信息的来源。对于8086CPU,当CPU内部发生以下情况时,将产生相应的中断信息,其对应的中断类型码同样列出。

  • 除法错误,例如执行div指令产生的除法溢出,中断类型码为0;
  • 单步执行,中断类型码为1;
  • 执行into指令,中断类型码为4;
  • 执行int指令,该指令格式为int n,指令中的n为字节型立即数,是提供给CPU的中断类型码。

中断处理程序

CPU收到中断信息后,需要对中断信息进行处理。人为编写的可用来处理中断信息的程序被称为中断处理程序。一般来说,需要针对不同的中断信息编写不同的处理程序。根据CPU的涉及,中断类型码的作用就是定位中断处理程序

中断向量表

CPU使用8位的中断类型码通过中断向量表找到相应的中断处理程序的入口地址。中断向量表就是中断处理程序入口地址的列表。中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口,如下图所示。

1

中断向量表在内存中存放,对于8086PC机,中断向量表指定放在内存地址0处。从内存0000:0000~0000:03FF的1024个单元中存放着中断向量表。

在中断向量表中,一个表项存放一个中断向量,也就是一个中断处理程序的入口地址。对于8086CPU,这个入口地址包括段地址和偏移地址,所以一个表项占据两个字,高地址字存放段地址低地址字存放偏移地址

中断过程

通过中断类型码找到中断向量,并用其设置CS和IP的过程称为中断过程,该工作由CPU的硬件自动完成。8086CPU收到中断信息后,引发以下中断过程。

  • 取得中断类型码N;
  • pushf
  • TF=0,IF=0
  • push CS
  • push IP
  • (IP)=(N*4),(CS)=(N*4+2)

最后一步完成后,CPU开始执行中断处理程序。

中断处理程序&iret

中断处理程序编写的常规步骤如下:

  • 保存用到的寄存器
  • 处理中断
  • 恢复用到的寄存器
  • 使用iret指令返回

中断过程中,寄存器的入栈顺序是标志寄存器、CS和IP,而iret的出栈顺序是IP、CS和标志寄存器,与前者对应。iret指令的功能使用汇编语法描述为:

1
2
3
pop IP
pop CS
popf

编写0号中断处理程序

需求分析

重新编写一个0号中断处理程序,它的功能是在屏幕中间显示”overflow!”,然后返回到操作系统。

经过对除法溢出错误即0号中断处理程序的分析,我们将题目需求细化为以下几部分:

  • 编写可以在屏幕中间显示”overflow!”的中断处理程序do0;
  • 将do0送入内存0000:0200处;
  • 将do0的入口地址0000:0200存储在中断向量表0号表项中。

程序框架

1
2
3
4
5
6
7
8
9
10
11
12
assume cs:code
code segment
start: do0安装程序
设置中断向量表
mov ax,4c00h
int 21h

do0: 显示字符串"overflow!"
mov ax,4c00h
int 21h
code ends
end start

安装

使用rep movsb指令实现中断处理程序的安装需要确定如下信息:

  • 传送的原始位置,段地址code,偏移地址offset do0
  • 传送的目的位置,本例中为0:200
  • 传送的长度即do0部分代码的长度;
  • 传送的方向:正向。

明确后的安装程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset do0 ;设置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址

mov cx,offsetdo0end-offset do0 ;设置cx为传输长度,"-"是编译器识别的运算符号

cld ;设置传输方向为正
rep movsb

设置中断向量表
mov ax,4c00h
int 21h

do0: 显示字符串"overflow!"
mov ax,4c00h
int 21h
do0end: nop
code ends
end start

设置中断向量

0号表项的地址为0:0,其中0:0字单元存放偏移地址,0:2字单元存放段地址。程序如下:

1
2
3
4
mov ax,0
mov es,ax
mov word ptr es:[0*4],200h
mov word ptr es:[0*4+2],0

最终程序及运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
assume cs:code
code segment
start:
; 安装
mov ax,cs
mov ds,ax
mov si,offset do0
mov ax,0
mov es,ax
mov di,200h
mov cx,offset do0end-offset do0
cld
rep movsb

; 设置中断向量表
mov ax,0
mov es,ax
mov word ptr es:[0*4],200h
mov word ptr es:[0*4+2],0

mov ax,4c00h
int 21h

; 字符串"overflow!"应存放在一段不会被覆盖的空间中
; 当发生溢出时,jmp指令引导程序直接跳转执行do0start代码段
do0: jmp short do0start
db "overflow!"

do0start:
mov ax,cs
mov ds,ax
mov si,202h

mov ax,0b800h
mov es,ax
mov di,12*160+36*2

mov cx,9
s: mov al,[si]
mov es:[di],al
inc si
add di,2
loop s

mov ax,4c00h
int 21h
do0end: nop
; 溢出例测试
divtest:
mov ax,1000h
mov bh,1
div bh

code ends
end start

运行结果如下:

2

单步中断

CPU完成指令执行后,如果检测到标志寄存器的TF位为1,则产生单步中断,引发中断过程。单步中断的中断类型码为1,引发的中断过程如下:

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

CPU提供单步中断,为单步跟踪程序的执行过程提供了实现机制。一般情况下,CPU在执行完当前指令后,若检测到中断信息则立即响应中断,引发中断过程。但是在某系情况下不立即响应,如执行完向ss寄存器传送数据的指令后,即便发生中断CPU也不会响应,这是因为ss:sp联合指向栈顶,故对它们的设置应连续完成(粘连操作)。

0x01 int指令

除前文提到的常见中断信息外,还可以通过int指令引发中断过程。

int指令格式为int n,n为中断类型码,其功能是引发中断过程。int指令的最终功能和call指令类似,都是调用一段中断例程。

BIOS&DOS中断例程

BIOS称为基本输入输出系统,存放于系统板的ROM中,主要包含以下几部分内容。

  • 硬件系统的检测和初始化程序;
  • 外部中断和内部中断的中断例程;
  • 用于对硬件设备进行I/O操作的中断例程;
  • 其他和硬件系统相关的中断例程。

和硬件设备相关的DOS中断例程中,一般都调用了BIOS的中断例程。BIOS和DOS提供的中断例程都使用寄存器ah来传递内部子程序的编号。

BIOS&DOS中断例程安装

  • 开机后,CPU加电,初始化(CS)=0FFFFH,(IP)=0。CPU执行FFFF:0处的指令,转去执行BIOS中的硬件系统检测和初始化程序。
  • 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。对于BIOS所提供的中断例程,由于其固化到ROM中,故只需将入口地址登记在中断向量表中即可。
  • 硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导。随后将计算机交由操作系统控制。
  • DOS启动后,除完成其他工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量。

BIOS中断例程应用

int 10h中断例程是BIOS提供的中断例程,其中包含了多个和屏幕输出相关的子程序。以设置光标位置为例。

1
2
3
4
5
mov ah,2		; 置光标
mov bh,0 ; 第0页
mov dh,5 ; dh中放行号
mov dl,12 ; dl中放列号
int 10h

(ah)=2表示调用第10h号中断例程的2号子程序,功能为设置光标位置,可以提供光标所在的行号、列号,和页号作为参数。

(bh)=0,(dh)=5,(dl)=12,设置光标的第0页,第5行,第12列。

DOS中断例程应用

int 21h中断例程是DOS提供的中断例程,其中包含了DOS提供给程序员在编程时调用的子程序。以程序返回功能为例。

1
2
3
mov ah,4ch		; 程序返回
mov al,0 ; 返回值
int 21h

(ah)=4ch表示调用第21h号中断例程的4ch号子程序,功能为程序返回,可以提供返回值作为参数。

0x02 端口

各种存储器都和CPU的地址线、数据线、控制线相连。CPU在操控它们的时候,把它们看作一个由若干存储单元组成的逻辑存储器即内存地址空间。

PC机系统中,和CPU通过总线相连的芯片除各种存储器外,还包含以下三类:

  • 各种接口卡上的接口芯片,它们控制接口卡进行工作;
  • 主板上的接口芯片,CPU通过它们对部分外设进行访问;
  • 其他芯片,用来存储相关的系统信息,或进行相关的输入输出处理。

从CPU的角度,将这些寄存器都当作端口,对它们进行统一编址,从而建立一个统一的端口地址空间。每个端口在地址空间中都对应一个地址。CPU可以直接从以下三个地方读写数据:

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

端口读写

CPU通过端口地址来定位端口,端口地址和内存地址一样,通过地址总线来传送。在PC系统中,CPU最多可以定位64KB个不同的端口,端口地址范围为0~65535。

端口的读写指令有in和out两条,分别用于从端口读取数据和往端口写入数据。

注意,在in和out指令中,只能使用ax或al来存放从端口中读入的数据或要发送到端口中的数据。访问8位端口时用寄存器al,访问16位端口时用寄存器ax

CMOS RAM芯片

该芯片特征如下:

  • 包含一个实时钟和一个有128个存储单元的RAM存储器。
  • 芯片靠电池供电,关机后RAM中的信息不丢失。
  • 128个字节的RAM中,内部实时钟占用0~0dh单元来保存时间信息,其余大部分单元用于保存系统配置信息,供系统启动时BIOS程序读取。
  • 该芯片内部有两个端口,端口地址分别为70h和71h。CPU通过这两个端口来读写CMOS RAM。
  • 70h为地址端口,存放要访问CMOS RAM单元的地址;71h为数据端口,存放从选定的CMOS RAM单元中读取的或要写入其中的数据

该芯片中存放着当前时间:年、月、日、时、分、秒。这6个信息的长度均为1字节,存放单元为:秒:0,分:2,时:4,日:7,月:8,年:9,这些数据以BCD码的方式存放。高4位BCD码表示十位,低4位的BCD码表示个位。

shl&shr指令

shl是逻辑左移指令,功能为:

  • 将一个寄存器或内存单元中的数据向左移位;
  • 将最后移出的一位写入CF中;
  • 最低位用0补充。

shr是逻辑右移指令,功能于shl相反,最后使用0补充最高位。

若移动位数大于1,必须将移动位数放在寄存器cl中。

0x03 外中断

CPU通过端口和外部设备进行联系。当外设的输入发生时,产生外中断;待CPU执行完当前指令后,可以检测到发送过来的外中断信息,引发中断过程,处理外设的输入。

PC系统中,外中断源包含可屏蔽中断和不可屏蔽中断两类。

可屏蔽中断

可屏蔽中断是CPU可以不响应的外中断。当CPU检测到可屏蔽中断信息时,检查IF标志位信息。若IF=1则引发中断过程,否则不响应可屏蔽中断。中断过程中常将IF置0,该方法可以禁止其他可屏蔽中断。

可以通过sti指令和cli指令设置IF标志位的值。

不可屏蔽中断

不可屏蔽中断是CPU必须响应的外中断。CPU检测到不可屏蔽中断信息时,在执行完当前指令后立即响应,引发中断过程。对于8086CPU,不可屏蔽中断的中断类型码为2。中断过程为:

  • 标志寄存器入栈,IF=0,TF=0;
  • CS、IP入栈;
  • (IP)=(8),(CS)=(0AH)

CPU处理外设输入

  • 外设的输入送入端口;
  • 向CPU发出外中断信息;
  • CPU检测到可屏蔽中断信息,若IF=1,CPU在执行完成当前指令后响应中断,执行相应的中断例程。
  • 可在中断例程中实现对外设输入的处理
  • 按键和松开按键时都会产生扫描码,随后扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60h。一般来说,按下按键时产生的扫描码称为通码,松开按键时产生的扫描码为断码。断码=通码+80h
  • 端口和中断机制是CPU进行I/O的基础。
请作者吃个小鱼饼干吧