x86指令编码简述(机器码)
机器码的格式结构:
一般的 x86 机器指令格式,包含了一个指令前缀字节、操作码、Mod R/M 字节、伸缩索引字节(SIB)、地址位移和立即数。指令按小端顺序存放,因此前缀字节位于指令的起始地址(基本遇不到)。
每条指令都必须要有一个操作码,而其他字段则是可选的。少数指令包含了全部字段,平均来看,绝大多数指令都有 2 个或 3 个字节。
指令字段解析:
1):指令前缀覆盖默认操作数大小。(基本遇不到)
2):操作码指定指令的特定变体,比如按照使用的参数(寄存器|操作数)类型,指令 ADD 有 9 种不同的操作码。
(具体操作码看参考手册)
3):Mod R/M 字段指定寻址模式和操作数,符号 “R/M” (register/memory) 代表的是寄存器和模式。
4):伸缩索引字节(scale index byte, SIB)用于计算数组索引偏移量。
5):地址位移字段保存了操作数的偏移量,在 “基址-偏移量” 或 “基址-变址-偏移量” 寻址模式中,该字段还可以与 “基址或变址寄存器” 相加。
6):立即数字段保存了常量操作数。
操作码的组成:在多数操作码中,常使用某些位来指示某些信息,下面列举出部分位及其含义:
(1:在参考手册 opcodes.html 的 Opcode 列中已经不需要考虑 d 和 s 了(定死了),但还是理解一下)
(2:上面操作码组成中只列出了 d、s、w,还有很多其它的如 rrr、sss 等,具体看操作码手册附录)
|_____________OP_______________|__d__|__w__|
|_____________OP_______________|__s__|__w__| <--此格式用于立即寻址方式
w (word) 值表示紧跟的操作数的大小类型:
w=1 时 对字来操作(如果是寄存器的话就是 16 位寄存器)
w=0 时 对字节来操作(如果是寄存器的话就是 8 位寄存器)
d 值在双操作数指令中才有效:(两个都是寄存器时以目的寄存器为准,不包括立即数的双操作数下)
d=1 时 有且只有一个寄存器用于目的操作数
d=0 时 有且只有一个寄存器用于源操作数
s 值只要有立即数存在就使用:
s=1 时 立即数为 8 位,但要求扩展成 16 位数
s=0 时 当指令作字节操作/有 16 位立即数
Mod R/M 字节标识操作数寻址方式:(不包括立即数的双操作数指令的情况下)
| mod | reg | r/m |
|_____|_____|_____|_____|_____|_____|_____|_____|
reg 指定寄存器编号,在不包括立即数的双操作数指令的情况下,规定必须有一个操作数在寄存器中。如果只有一个寄存器,则该寄存器由 reg 字段指定编号。如果两个操作数都是寄存器,则以目的寄存器为准。并与操作码字节中的 w 位相组合进一步确定寄存器大小类型。(reg 的 rrr 确定 8 个通用寄存器编号,操作码的末尾 w 位确定寄存器的大小)
Mod 值指定操作数的寻址模式:
Mod(oo)
位移
00
如果 mmm = 110,则操作码后面跟 16 位偏移
01
操作码后面是一个 8 位有符号的偏移
10
操作码后面是一个 16 位有符号的偏移
11
R/M (mmm)字段包含的是寄存器编号
rrr W=0 W=1 reg32
000 : AL : AX : EAX001 : CL : CX : ECX010 : DL : DX : EDX011 : BL : BX : EBX100 : AH : SP : ESP101 : CH : BP : EBP110 : DH : SI : ESI111 : BH : DI : EDI
mmm : Function
000 : DS:[BX+SI]001 : DS:[BX+DI]010 : SS:[BP+SI]011 : SS:[BP+DI]100 : DS:[SI]101 : DS:[DI]110 : SS:[BP]111 : DS:[BX]
sss : Segment Register
000 : ES001 : CS010 : SS011 : DS100 : FS (Only 386+)101 : GS (Only 386+)
Mod R/M 字节与内存寻址模式探究:
如果 Mod R/M 字节只用于标识寄存器操作数(mod=11),那么 Intel 指令编码就会相对简单。实际上 Intel 汇编语言有着各种各样的内存寻址模式,这就使得 Mod R/M 字节编码相当复杂。
Mod R/M 字节可以指定 256 个不同组合的操作数,下表只列岀了 mod 字段 为 00 时的 Mod R/M 字节的值:
(Mod 在橙色块,Reg 在上面绿色块,R/M 在紫色块,黄色块是总的加起来的 Mod R/M 字节值,蓝色块是要操作的 R/M 字段对应的源操作数。根据 Mod R/M 字节内部结构,阅读顺序是 mod—>reg—>r/m)
字节
AL
CL
DL
BL
AH
CH
DH
BH
字
AX
CX
DX
BX
SP
BP
SI
DI
寄存器ID
000
001
010
011
100
101
110
111
Mod
R/M
Mod R/M 值
有效地址
00
000
00
08
10
18
20
28
30
38
[BX+SI]
001
01
09
11
19
21
29
31
39
[BX+DI]
010
02
0A
12
1A
22
2A
32
3A
[BP+SI]
011
03
0B
13
1B
23
2B
33
3B
[BP+DI]
100
04
0C
14
1C
24
2C
34
3C
[SI]
101
05
0D
15
1D
25
2D
35
3D
[DI]
110
06
0E
16
1E
26
2E
36
3E
16 位偏移量
111
07
0F
17
1F
27
2F
37
3F
[BX]
Mod R/M 编码解析:
mod 字段指定寻址模式的集合,比如 mod=00 时有 8 种可能的 R/M 数值(000b〜111b),上表的 “有效地址栏” 指定了对应源操作数类型。
举例编码 MOV AX, [Si]:
根据上表 “有效地址栏” 可得 Mod 位为 00,R/M 位为 100,AX 的寄存器编号为 000,因此完整的 Mod R/M 字节为 00 000 100。(即 04h)
mod
reg
r/m
00
000
100
附录一:32 位寻址模式
oo
mmm
rrr
Description
00
000
DS:[EAX]
00
001
DS:[ECX]
00
010
DS:[EDX]
00
011
DS:[EBX]
00
100
000
DS:[EAX+scaled_index]
00
100
001
DS:[ECX+scaled_index]
00
100
010
DS:[EDX+scaled_index]
00
100
011
DS:[EBX+scaled_index]
00
100
100
SS:[ESP+scaled_index]
00
100
101
DS:[disp32+scaled_index]
00
100
110
DS:[ESI+scaled_index]
00
100
111
DS:[EDI+scaled_index]
00
101
DS:disp32
00
110
DS:[ESI]
00
111
DS:[EDI]
01
000
DS:[EAX+disp8]
01
001
DS:[ECX+disp8]
01
010
DS:[EDX+disp8]
01
011
DS:[EBX+disp8]
01
100
000
DS:[EAX+scaled_index+disp8]
01
100
001
DS:[ECX+scaled_index+disp8]
01
100
010
DS:[EDX+scaled_index+disp8]
01
100
011
DS:[EBX+scaled_index+disp8]
01
100
100
SS:[ESP+scaled_index+disp8]
01
100
101
SS:[EBP+scaled_index+disp8]
01
100
110
DS:[ESI+scaled_index+disp8]
01
100
111
DS:[EDI+scaled_index+disp8]
01
101
SS:[EBP+disp8]
01
110
DS:[ESI+disp8]
01
111
DS:[EDI+disp8]
10
000
DS:[EAX+disp32]
10
001
DS:[ECX+disp32]
10
010
DS:[EDX+disp32]
10
011
DS:[EBX+disp32]
10
100
000
DS:[EAX+scaled_index+disp32]
10
100
001
DS:[ECX+scaled_index+disp32]
10
100
010
DS:[EDX+scaled_index+disp32]
10
100
011
DS:[EBX+scaled_index+disp32]
10
100
100
SS:[ESP+scaled_index+disp32]
10
100
101
SS:[EBP+scaled_index+disp32]
10
100
110
DS:[ESI+scaled_index+disp32]
10
100
111
DS:[EDI+scaled_index+disp32]
10
101
SS:[EBP+disp32]
10
110
DS:[ESI+disp32]
10
111
DS:[EDI+disp32]
附录二:寻址模式与机器码(假设 myWord 的起始地址偏移量为 0102h)
指令
机器码
寻址模式
mov ax, my Word
A1 02 01
直接
mov my Word,bx
89 IE 02 01
直接
mov [di],bx
89 ID
变址
mov [bx+2],ax
89 47 02
基址 - 偏移量
mov [bx+si],ax
89 00
基址 - 变址
mov word prt [bx+di+2], 1234h
C7 41 02 34 12
基址 - 变址 - 偏移量
附录三:8 位和 16 位 MOV 指令所有的指令格式和操作码:(根本还是对照着参考附录来)
操作码
指令
说明
操作码
指令
说明
88/r
MOV eb, rb
字节寄存器送 EA 字节操作数
8E/2
MOV SS, rw
字寄存器送 SS
89/r
MOV ew, rw
字寄存器送 EA 字操作数
8E/3
MOV DS, mw
内存字送 DS
8A/r
MOV rb, eb
EA 字节操作数送字节寄存器
8E/3
MOV DS, rw
字寄存器送 DS
8B/r
MOV rw, ew
EA 字操作数送字寄存器
A0 dw
MOV AL, xb
字节变量(偏移量为 dw)送 AL
8C/0
MOV ew, ES
ES 送 EA 字操作数
A1 dw
MOV AX, xw
字变量(偏移量为 dw)送 AX
8C/1
MOV ew, CS
CS 送 EA 字操作数
A2 dw
MOV xb, AL
AL 送字节变量(偏移量为 dw)
8C/2
MOV ew, SS
SS 送 EA 字操作数
A3 dw
MOV xw, AX
AX 送字寄存器(偏移量为 dw)
8C/3
MOV ew, DS
DS 送 EA 字操作数
B0+rb db
MOV rb, db
字节立即数送字节寄存器
8E/0
MOV ES, mw
内存字送 ES
B8+rw dw
MOV rw, dw
字立即数送字寄存器
8E/0
MOV ES, rw
字寄存器送 ES
C6 /0 db
MOV eb, db
字节立即数送 EA 字节操作数
8E/2
MOV SS, mw
内存字送 SS
C7 /0 dw
MOV ew, dw
字立即数送 EA 字操作数
下表是上面中缩写符号的补充信息:
/n:
操作码后面跟一个 Mod R/M 字节,该字节后面可能再跟立即数和偏移量字段。数字 n( 0〜7 )为 Mod R/ M 字节中 reg 字段的值
/r:
操作码后面跟一个 Mod R/M 字节,该字节后面可能再跟立即数和偏移量字段
db:
操作码和 Mod R/M 字节后面跟一个字节立即操作数
dw:
操作码和 Mod R/M 字节后面跟一个字立即操作数
+rb:
8 位寄存器的编号(0〜7 ),与前面的十六进制字节一起构成 8 位操作码
+rw:
16 位寄存器的编号(0〜7 ),与前面的十六进制字节一起构成 8 位操作码
实战部分机器指令类型:
单字节指令:(空操作数或隐含操作数)
没有操作数或只有一个隐含操作数的指令是最简单的指令,这种指令只需要操作码,其值由处理器的指令集直接确定,没有任何变化可言。
下表列出了几个常见的单字节指令:
指令
操作码
指令
操作码
AAA
37
LODSB
AC
AAS
3F
XLAT
D7
CBW
98
INC DX
42
在这些指令中,INC DX 指令好像是不应该岀现的,它出现的原因是:指令集的设计者决定为某些常用指令提供独特的操作码。其结果是,为了代码量和执行速度要对寄存器增量操作进行优化(就是对常用指令给特权,提高执行速度)。
立即数送寄存器指令:(MOV Reg Imm 1011wrrr)
举例由指令手册可知,将一个立即数送寄存器的 MOV 指令格式为 MOV Reg Imm 1011wrrr ,其中操作码可以拆分成 B8 + wrrr,B8 是 MOV Reg Imm 的基指令 (10110000),+wrrr 表示变体类型。
w 表示对字操作(16位寄存器),rrr 指明寄存器编号(0〜7)。Imm 立即操作数(常数)按照小端顺序(起始地址为最低字节)添加到指令
【示例 1】MOV AX, 1 机器指令为 B8 01 00(十六进制),编码过程如下:
1) 立即数送 16 位寄存器的操作码为 B8
2) AX 的寄存器编号为 0,将 0 加上 B8(参见上表所示)
3) 立即操作数(0001)按小端顺序添加到指令(01, 00)(因为操作数是单独的类型,字大小,所以在最小 byte 划分中逆序存放)。
【示例 2】MOV BX, 1234h 机器指令为 BB 34 12,编码过程如下:
1) 立即数送 16 位寄存器的操作码为 B8。
2) BX 的寄存器编号为 3,将 3 加上 B8 得到操作码 BB。
3) 立即操作数字节为 34 12。
寄存器送寄存器指令:(MOV Reg Reg 1000101woorrrmmm)
举例由指令手册可知,寄存器送寄存器指令指令格式为 MOV Reg Reg 1000101woorrrmmm ,其中操作码可以拆分成 BA + w + Mod R/M 。
BA 是 MOV Reg Reg 的基指令 (10001010),+w 表示操作数类型。Mod R/M 字节标识操作数寻址方式,这里由于两个都是寄存器寻址,所以 Mod(oo)字段值为 11,reg 和 r/m 字段分别指定目的寄存器和源寄存器编号。
比如,MOV AX, BX 的机器码为 8B C3,解析如下:
寄存器送其他操作数的 16 位 MOV 指令的 Intel 编码为 8B/r,其中 /r 表示操作码后面带一个 Mod R/M 字节。(基指令 8A (10001010) 加 w=1 (16 位寄存器),所以是 8B (10001011))
Mod R/M 的值为 C3,它包含如下字段:
mod
reg
r/m
11
000
011
位 6〜7 是 mod 字段,指定寻址模式。mod 字段为 11 表示 r/m 字段包含的是一个寄存器编号(r/m 字段指定源操作数)。位 3〜5 是 reg 字段,指定目的操作数。在本例中,AX 就是编号为 000 的寄存器。位 0〜2 是 r/m 字段,指定源操作数。本例中,BX 是编号为 011 的寄存器。
下表列出了更多使用 8 位和 16 位寄存器操作数的例子:
(al 和 ax 分别是 8 位和 16 位寄存器,w 分别 为 0 和 1,所以才一个是 8A 一个是 8B)
指令
机器码
mod
reg
r/m
mov ax, dx
8B C2
11
000
010
mov al, dl
8A C2
11
000
010
mov ex, dx
8B CA
11
001
010
mov cl, dl
8A CA
11
001
010