2. x87 浮点指令的编码方式


上一页 返回目录 下一页

先看 2 个例子:

fadd st(0), st(1)

fadd dword ptr [eax]

 

这 2 条指令只是 operand 不同,指令的 opcode 都是相同的。

第 1 条指令的编码为: d8 c1

d8
11
000
001
opcode
mod
reg
r/m
ModRM = c1

 

第 2 指令的编码为:d8 00

d8
00
000
000
opcode
mod
reg
r/m
ModRM = 00

和通用指令的寻址模式一样。

ModRM.mod = 11 提供 registers 寻址,此时, ModRM.r/m = 001 则提供的是 st(1) 浮点寄存器。

ModRm.mod = 00 提供的是 [base] 这种 memory 寻址模式,ModRM.r/m = 000 是 [eax] 操作数。

 

 

1、 ModRM 字节的作用

在 x87 浮点指令编码中,ModRM 字节同时提供 2 个作用:

  1. ModRM.reg 用来确定 opcode

  2. ModRM.r/m 配合 ModRM.mod 进行 operands 寻址

可以看出:这和通用指令编码中的 Group 属性 opcode 的指令没什么两样。

下面这个表格列出 ModRM.reg 的定位作用

opcode
ModRM.reg
指令
D8
000
fadd
001
fmul
010
fcom
011
fcomp
100
fsub
101
fsubr
110
fdiv
111
fdivr

(1)每一个 ModRM.reg 值对应一条具体的指令。

而 Opcode 码大部分只是起分门别类作用,实际由 ModRM.reg 进行确定指令,如上面的表格显示,opcode 为 D8 ,而 ModRM.reg 用来确定最终的指令。

 

(2)下面看一看当 ModRM.mod = 11 时,ModRM.r/m 提供浮点寄存器

ModRM.mod
ModRM.r/m
registers
11
000
st(0)
001
st(1)
010
st(2)
011
st(3)
100
st(4)
101
st(5)
110
st(6)
111
st(7)

可见 x87 指令中的 ModRM 与通用指令中的 ModRM 作用是一样的。只是寄存器不同。

 

(3)当 ModRM.mod ≠ 11 时, ModRM.r/m 提供 memory 寻址

在这种情况下,x87 指令的 operand 是 memory 操作数,和 通用指令中的内存寻址是完全一样的。

有关 ModRM 寻址,详见:ModRM 寻址模式

 

 

2、 operands 寻址

x87 指令的 operands 寻址分三种情况:

(1) 无 operand 指令

只有当 ModRM.mod = 11 时,才会有无 operand 时的情况,而当 ModRM.mod ≠ 11 时,需要为指令提供 memory 操作数。

无 operand 时,指令 opcode 由 ModRM.reg 与 ModRM.r/m 联合确定。

fldz

上面这条指令是典型的无 operand 指令,它的 opcode 组成部分如下:

d9
11
101
110
opcode
mod
reg
r/m
ModRM = ee

其中 ModRM.reg = 101 配合 ModRM.r/m = 110 来确定 opcode

 

(2)单 operands 指令

x87 指令中单 operand 的情况下,这个 operand 是 memory 操作数。

ModRM.mod ≠ 11 时,ModRM.r/m 提供 memory 寻址,前面已经提到:这个 memory 操作数寻址与通用指令中的 memory 操作数寻址是完全一样的。

fld dword ptr [rax]

上面这条指令是典型的单 operand 指令,它的 opcode 组成部分如下:

d9
00
000
000
opcode
mod
reg
r/m
ModRM = 00

这条指令的最终编码是: d9 00

对于 memory 操作数,legacy prefix 与 REX prefix 都可以使用

 

fld dword ptr gs:[r10]

这条指令使用了 segment override prefix 和 REX prefix,它的编码组成部分如下:

65
0100
0
0
0
1
d9
00
000
010
legacy prefix
4
W
R
X
B
opcode
mod
reg
r/m
REX.[4WRXB] = 41
ModRM = 02

这条指令最终的编码是:65 41 d9 02

 

(3)2 个 operands

这 2 个 operands 都是浮点寄存器,且其中 1 个 operand 必定是 st(0) 寄存器,另 1 个 operand 则由 ModRM.r/m 提供寻址。

fadd st(0), st(1)

上面这条指令两个 operand 都是 register,其中 destination 操作数是 st(0), source 操作数是 st(1)

它的 opcode 组成部分如下:

d8
11
000
001
opcode
mod
reg
r/m
ModRM = c1

这条指令的最终编码是:d8 c1

 

 

 

3、 x87 指令在不同模式下

x87 浮点寄存器在 16 位、32位以及 64 位模式下宽度都是一样的。

但是对于 memory 操作数来说,情形和通用指令一样,分为 16 位地址模式、32 位地址模式以及 64 位地址模式。取决于指令的 effective address-size

 

以指令编码 d9 00 为例,在不同模式下的表现形式:

指令编码
模式
指令形式
描述
d9 00
16 位
fld dword ptr [bx+si]
16 位地址模式
32 位
fld dword ptr [eax]
32 位地址模式
64 位
fld dword ptr [rax]
64 位地址模式

和通用指令一样,对于 memory 寻址上可以进行 address-size override 操作。

指令 fld dword ptr [eax] 在 16 位模式下,它的编码是:67 d9 00 (使用 67 进行 address size override)

 

以指令 fadd st(0),st(1) 为例,在不同模式下的编码是:

指令
模式
指令编码
描述
fadd st(0),st(1)
16 位
d8 c1
寄存器宽度不变
32 位
d8 c1
64 位
d8 c1

浮点寄存器没有宽度之分,在哪个模式下都是一样的。

如同 mmx 与 xmm 寄存器一般。

 

 

 

4、 x87 指令编码与通用指令编码的异同

x87 指令编码方式与通用指令完全一致的。但是 x87 指令没有 immediate 域, 是因为 x87 指令不支持立即数寻址。

从上一篇的 x87 指令格式中了解到:opcode 与 ModRM 是必须的。这一点和通用指令有些区别:在通用指令中 ModRM 字节是可选的。

 

 

上一页 返回目录 下一页


mik 写于 2009-05-17 00:38