80386汇编-指令集
9unk Lv5

这一章主要学习 80386 指令集,并回顾 8086 指令。

数据传送指令

数据传送:寄存器、内存单元、I/O端口之间传送数据和地址。
80386 分为 4 种:通用传送指令、累加器专用传送指令和标志传送指令。

通用传送指令组

MOV 指令

  1. 指令简介
    1

  2. 指令的语法及描述
    2

MOVZX 和 MOVSX 指令

在 8086 汇编中,都是用 XOR 指令和符号扩展指令(CBW和CWD)把高位的符号位初始化。但是在 80386 中高 16 位是没有寄存器的,没办法直接传送无符号数(有符号数)并初始化寄存器。为了解决这个问题,80386 又设计 MOVZX 和 MOVSX 指令来传送数据。

MOVZX 指令

  1. 简介及语法
    3
    5

  2. 案例
    4

MOVSX 指令

  1. 简介及语法
    6
    7
    8

  2. 案例
    9

CBW/CWD/CDQ

18

19

20

  • CBW 是 al 为数据,ah扩展符号位。
  • CWD 是 ax 为数据,dx扩展符号位。
  • CDQ 是 eax 为数据,edx扩展符号位。

XCHG

  1. 语法
    11
    12

  2. 案例
    13

堆栈操作指令

PUSH 和 POP

14
15

.386 指令集中的 PUSH 和 POP 指令,支持 16 位和 32 位数据入栈出栈。

PUSHA 和 PUSHAD

16

POPA 和 POPAD

17

.386 指令集中 PUSHA/POPA 是对16位寄存器入栈/出栈,PUSHAD/POPAD 是对32位寄存器入栈/出栈。

地址传送指令

LEA 将内存有效地址,存储到寄存器中。
LDS、LES、LFS、LGS、LSS 将内存中的数据作为内存地址分别传送到相应的DS、ES、FS、GS、SS段寄存器,同时也会传输到目的操作数中。
32

LEA

21

LDS

22

LES

23

LFS

24

LGS

25

LSS

26

标志传送指令

LAHF 和 SAHF

10

PUSHF/PUSHFD

27

  • PUSHF 压入 eflags 低16位值
  • PUSHFD 压入 eflags(32位) 的值

POPF/POPFD

28

  • POPF 弹出 eflags 低16位值
  • POPFD 弹出 eflags(32位) 的值

累加器专用传送指令

IN

29

OUT

30

XLAT

31

算数运算指令

加减指令

ADD

35

ADC

36

INC

37

SUB

38

SBB

39

DEC

40

CMP

41

NEG

42

乘除指令

MUL

34

MUL 指令执行后需要注意 CF 位,如果 CF=1,就要关注 DX 或 EDX 的值。

IMUL

43
44

DIV

45
46

IDIV

47
48

在进行有符号数除法指令运算之前需要根据情况执行 CBW、CWD 或 CDQ 指令。

十进制算数调整指令

DAA,DSA,AAA,AAS,AAM,AAD 的功能与 8086 相同。

AAA

49

AAS

50

AAM

51
52

AAD

53

逻辑运算和位移指令

逻辑运算指令组

NOT

55

AND

54

OR

56

XOR

57

TEST

58
59

位移指令组

60
61

逻辑位移(SHL/SHR)指令

  1. SHL
    62

  2. SHR
    63

算数位移(SAL/SAR)指令

64

循环位移(ROL/ROR)指令

  1. ROL
    65
    66

  2. ROR
    67

RCL 和 RCR指令

68
69

SHLD/SHRD 指令

70
71
72

控制转移指令

转移指令组

无条件转移指令

JMP 指令分为段内直接、段内间接、段间直接,段间间接四种
扩展形式:扩展的无条件转移指令的转移目的转移目的地址偏移采用32位表示,段间转移目的地址采用 48 位全指针形式表示。
因为实模式段内偏移最大 64K,所以虽然 80386 允许把 32 位段内偏移传送到 EIP,实际不需要。
JMP EAX ;实模式下有效,但是EAX不能超过64K
在保护模式下,段内无条件转移指令的准一方法未变,但段间无条件转移指令的执行细节比较复杂,保护模式章节会讲解。

条件转移指令

73

74

75

76

循环指令组

LOOP指令

77

LOOPZ和LOOPE指令

78
79

LOOPNZ和LOOPNE指令

80

过程调用和返回指令组

81

中断调用和中断返回指令组

实模式下,中断调用 INT 功能和 8086 相同。
保护模式下,中断嗲用指令 INT 把标志寄存器 EFLAGS,CS 和 EIP 压入堆栈,即压入堆栈 3 个双字,在压入 CS 时有扩展到 32 位,高 16 位为0,具体细节复杂,保护模式章节介绍。

中断返回指令 IRET 有扩展和非扩展两种形式
实模式下,总是使用非扩展形式,与 8086 相同
保护模式下,使用扩展形式,与中断指令 INT 对应,具体细节较复杂,保护模式章节介绍。

IRETD 指令先弹出一个 32 位的 EIP 值,然后再弹出一个32位的标志寄存器值。

串操作指令

基本串操作指令

82

重复前缀

83

方向标志位

84

串输入指令

INSB ;输入字节(Byte)
INSW ;输入字(Word)
INSD ;输入双字(Dword)

以上三条串输入指令的格式可同一写为如下格式:

INS DSTS,DX

汇编程序根据目的串 DSTS 类型决定使用字节输入指令、字输入指令或双字输入指令。串输入指令不影响标志位。

串输出指令

OUTSB ;输出字节(Byte)
OUTSW ;输出字(Word)
OUTSD ;输出双字(DWORD)

串输出指令把由 DS:SI 所指的源串中的一个字符,输出到由 DX 给出的端口,同时根据方向标志位 DF 和字符类型调整SI(或ESI)

在汇编语言中,上面三条输出指令的格式可同一写为:

OUTS DX,SRCS

汇编程序根据源串 SRCS 类型决定使用字节输出指令、子输出指令或双字输出指令。串输出指令不影响标志位。

高级语言支持指令

高级语言支持指令始于 80186,它们用于简化支持高级语言的某些特征。共有3条这样的指令,它们是:BOUND、ENTER 和 LEAVE。

建立和释放堆栈框架指令

在 C 和 PASCAL 等高级语言中,函数或过程不仅通过堆栈传递入口参数,而且它们的局部变量也被安排在堆栈中,为了方便获取入口参数和准确地存取局部变量,就要建立合适的堆栈框架。

例如,如下C函数和对应的汇编语言过程,就可以看到堆栈框架的建立和使用情况。

1
2
3
4
5
6
sum(int x,int y)
{
int sum;
sum=x+y;
return(sum);
}

上面的函数 sum 对应的汇编语言过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
_sum proc near
push ebp
mov ebp,esp
sub esp,4 ;建立堆栈框架
;
mov eax,word ptr [ebp+4] ;取参数x
add eax,word ptr [bp+8] ;加参数y
mov word ptr [ebp-4],eax ;保存x+y之和到局部变量sum
;
mov ax,word ptr [ebp-4] ;取返回参数
;
mov esp,ebp
pop ebp ;释放堆栈框架
;
ret ;返回
_sum endp

如果利用始于 80186 的建立和释放堆栈框架指令 ENTER 和 LEAVE,那么上面的汇编语言过程可优化如下:

1
2
3
4
5
6
7
8
9
10
11
_sum proc near
enter 4,0
;
mov eax,word ptr [ebp+4] ;取参数x
add eax,word ptr [bp+8] ;加参数y
mov word ptr [ebp-4],eax ;保存x+y之和到局部变量sum
mov ax,word ptr [ebp-4] ;取返回参数
;
leave
ret
_sum endp

检查数组下标界限指令 BOUND

检查数组下标界限指令 BOUND 的一般格式如下:

BOUND OPRD1,OPRD2

在 80386 之前,用于给待检车数组下标的操作数 OPRD1 是 16 位寄存器,用于给出数组下标上下界限的操作数 OPRD2 是 32 位存储器操作数,其中低字节含起始下标,高字节含结尾下标。从 80386 开始,OPRD1 还可以是 32 位寄存器,此时 OPRD2 只能是 64 位存储器操作数,其中低双字含起始下标,高双字含结尾下标。

该指令检车由 OPRD1 给出的有符号数是否由操作数 OPRD2 给出的数组界限之内。如果被检车的下标不在数组允许的范围内,那么产生类型号为5的异常(中断)。

指令 BOUND 不影响标志位。
85

条件字节设置指令

从 80386 开始新增了一组条件字节设置指令。这些指令根据一些标志位设置某个字节的内容为 1 或 0。

条件字节设置指令格式如下:

SETcc OPRD

其中 cc 是指助记符的一部分,用于表示条件,这些条件与条件转移指令中的条件相同;操作数 OPRD 只能是 8 位寄存器或者存储单元,用于存放测试的结果。

这些指令的功能是测试指令中规定的条件,若条件为 “真”,那么将目的操作数 OPRD 置1,否则置0
86
87

位操作指令

从 80386 开始增加了位操作指令。这些操作指令可以直接对一个二进制位进行测试、设置和扫描等操作。利用这些指令可以更有效地进行位操作。
位操作指令可分为位扫描指令组和位测试及设置指令组。

位测试及设置指令组

位测试和设置指令组含有如下 4 条指令:BT(位测试)指令、BTC(位测试并取反)指令、BTR(位测试并复位)指令和BTS(位测试并置位)指令。

这 4 条位测试和设置指令地格式如下:

BT OPRD1,OPRD2
BTC OPRD1,OPRD2
BTR OPRD1,OPRD2
BTS OPRD1,OPRD2

其中 OPRD1 可以是 16 位或 32 位通用寄存器和 16 位或 32 位存储单元,用于指定要测试地内容。OPRD2 必须是 8 位立即数或者与操作数 OPRD1 长度相等地通用寄存器,至于指定要测试的位。

位测试指令BT:功能是把被测试位的值送入标志位 CF;
位测试并取反指令BTC:把被测试位的值送到标志位 CF,并把被测试位取反;
位测试并复位指令BTR:功能是把被测试位的值送到标志位 CF,并把被测试位复位(即清0);
位测试并置位指令BTS:功能是把被测试位的值送入标志位 CF,并把被测试位置位(即置1)。

例如:

1
2
3
4
5
6
MOV BX,4567H    ;0100010101100111
MOV ECX,2
BT BX,CX ;CF=1,BX=4567H
BTC BX,3 ;CF=0,BX=456FH
BTR BX,CX ;CF=1,BX=456BH
BTS EBX,ECX ;CF=0,BX=456FH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.386
.model flat,stdcall
option casemap:none

INCLUDE masm32.inc
INCLUDE kernel32.inc

.data
IMAGEW DW 1234H,5678H ;0001 0010 0011 0100 0101 0110 0111 1000
IMAGED DD 12345678H

.code
main PROC
BT IMAGEW,4 ;CF=1
MOV CX,22
BTC IMAGEW,CX ;CF=1,[IMAGEW+2]=5638H
BTR IMAGED,6 ;CF=1,[IMAGED]=12345638H
MOVZX EAX,CX
BTS IMAGED,EAX ;CF=0,[IMAGED]=12345678H
INVOKE ExitProcess,0
main ENDP
END main

要特别指出,在这些位测试指令中,如果用于指定测试位号的操作数 OPRD2 是立即数,那么其值不应该超过被测试操作数 OPRD1 的长度,否则将产生未定义的位偏移量。这个规则允许规定在一个寄存器内的任何位移量,而且将存储器位串中的立即数位移量限制在规定存储单元字或双字之内。

但汇编程序可以支持对于内存位串的更大的立即数位偏移量,汇编程序可将该立即数位偏移量低 5 位(对于32位操作数)或低4位(对于16位操作数)作为及其指令中的操作数 OPRD2,将该立即数位移量的相应高位右移后加到内存位串开始单元的便宜上,作为机器指令中的操作数 OPRD1。

例:如下程序把寄存器 AL 的位0、2、4、6依次测试一次,所得的 8 位数保存在寄存器 AL中。

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
.386
.model flat,stdcall
option casemap:none

INCLUDE masm32.inc
INCLUDE kernel32.inc

.data
IMAGEW DW 1234H,5678H ;0001 0010 0011 0100 0101 0110 0111 1000
IMAGED DD 12345678H

.code
main PROC
MOV DL,0
MOV CX,4
MOV BX,0
NEXT:
BT AX,BX
SETC AH
OR DL,AH
ROR DL,1
INC BX
INC BX
LOOP NEXT
MOV AL,DL
INVOKE ExitProcess,0
main ENDP
END main

位扫描指令组

位扫描指令组含有如下2条指令:BSF(顺向位扫描)和BSR(逆向位扫描)
这两条指令格式如下:

BSF OPRD1,OPRD2
BSR OPRD1,OPRD2

其中,OPRD1 和 OPRD2 可以是 16 或 32 位通用寄存器(或者存储器),但 OPRD1 和 OPRD2 的长度必须相等。

顺向位扫描指令 BSF 的功能是从右向左(0位~15位或31位)扫描字或者双字操作数 OPRD2 中第一个含 “1” 的位,并把扫描到的第一个含 “1” 的位的位号送到 OPRD1。

逆向位扫描指令 BSR 的功能是从左向右(15位或31位~0位)扫描字或者双字操作数 OPRD2 中第一个含有 “1” 的位,并把扫描到的第一个含 “1” 的位号送到 OPRD1。

如果字或双字操作数 OPRD2 等于 0,那么零标志位 ZF 被置 1,操作数 OPRD1 的值不确定;否则零标志 ZF 被置 0 。

例如:

1
2
3
4
MOV EBX,12345678H
BSR EAX,EBX ;EAX=1CH,ZF=0
BSF DX,AX ;DX=2,ZF=0
BSF CX,DX ;CX=1,ZF=0

处理器控制指令

处理器控制指令用于设置标志、空操作与外部事件同步等。

设置标志指令组

33
设置中断允许标志 IF 的指令 CLI 和 STI,功能在实模式下保持与原先相同。在保护模式下它们是 I/O 敏感指令。

空操作指令(NOP)

空操作指令的功能是什么都不做。该指令就一个字节的操作码。利用该指令可“填补程序中的空白区”,使代码抱持连续。

外部同步指令和前缀

等待指令 WAIT

等待指令格式如下:

WAIT

该指令功能是等待直到BUSY引脚为高。BUSY由数值协处理器控制,所以该指令的 功能是等待数值协处理器,以便与它同步。该指令也能够检查数值协处理器是否故障。

封锁前缀 LOCK

封锁前缀 LOCK 可以锁定其后指令的目的操作数确定的存储单元,这是通过 LOCK 信号在指令执行期间一直保持有效而实现的。在多处理器环境中,使用这种方法可以保证指令执行时独占共享内存。

88

参考

Intel 80386 Reference Programmer’s Manual Table of Contents

Intel Instruction Set pages

[80x86汇编程序语言设计-80386指令集]

  • 本文标题:80386汇编-指令集
  • 本文作者:9unk
  • 创建时间:2022-11-13 23:53:00
  • 本文链接:https://9unkk.github.io/2022/11/13/80386-hui-bian-zhi-ling-ji/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!