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 : EAX
  • 001 : CL : CX : ECX
  • 010 : DL : DX : EDX
  • 011 : BL : BX : EBX
  • 100 : AH : SP : ESP
  • 101 : CH : BP : EBP
  • 110 : DH : SI : ESI
  • 111 : 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 : ES
  • 001 : CS
  • 010 : SS
  • 011 : DS
  • 100 : 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

 

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐一 · 林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值