首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

理解 x87 FPU 的执行环境

理解 x87 FPU 的执行环境

在这里,我们来了解一下 x86/x64 体系中的 x87 FPU 结构,包括下面几个物理资源:
  • 浮点数据寄存器:fpr0 - fpr7
  • 控制字寄存器:FCW(Control Word Register)
  • 状态字寄存器:FSW(Status Word Register)
  • tag 寄存器:FTW(Tag Word Register)
  • x87 last Instruction Pointer 寄存器
  • x87 last Data Pointer 寄存器
  • x87 last Opcode 寄存器
然而,x87 Environment 包括下面的要素:
  • 控制字寄存器:FCW(Control Word Register)
  • 状态字寄存器:FSW(Status Word Register)
  • tag 寄存器:FTW(Tag Word Register)
  • x87 last Instruction Pointer 寄存器
  • x87 last Data Pointer 寄存器
  • x87 last Opcode 寄存器
也就是说 x87 Environment 中并不包括 x87 FPU 的 data registers

1. last instruction pointer 与 last data pointerx87 last instruction pointer 是存储最后一条非控制指令的地址值,x87 last data pointer 是存储最后一条非控制指令的内存操作数地址值,如果最后一条非控制指令的操作数不是 memory 操作数,那么 x87 last data pointer 的值是无效的,未定义的。

2. 观察 last instruction pointer 与 last data pointer 表现现在我们来关注一下 last instruction pointer 与 last data pointer:
  • x87 last instruction pointer 和 last data pointer 都是 48 位 16:32segmentffset)模式
也就是说:48 位的寄存器高 16 位存放 segment 值,低 32 位存放 offset 值。
在 64-bit 模式下:
  • x87 last instruction pointer 和 last data pointer 都是 64 位的
在这种情况下,将不保存 last instruction selector 和 last data pointer selector 值。
下面的小节中,我将分别在 real mode,protected mode 以及 64-bit mode 下做实验,在实验中我将使用 fstenv 指令进行保存 x87 Environment
2.1 real mode 下的表现由于我们根据 fstenv 指令来观察 x87 Environment 因此根据 fstenv 指令的 memory 操作数,我们可以分为:
  • 16 位内存模式
  • 32 位内存模式
2.1.1 real mode 下的 16 位内存模式实际上 Intel 已经给出答案了,下图是摘自 Intel Manual:

现在我们可以用代码来验证这个图的正确性。
2.1.1.1 为测试做准备
        jmp 0x0100next - 0x1000)        ; for test ...
next:
        mov ax, 0x1000                        ; for test ...
        mov ds, ax
我在这里将 CS 设为 0x0100,而 DS 设为 0x1000 目的是想清楚地观察 last instruction pointer 和 last data pointer
2.1.1.2 执行 x87 指令
        fild word [mem16int]            ; for test DS:mem16int = 0x1000:XXXX
        fstenv [cs: state - 0x1000]              ; CS:stat = 0x0100:XXXX
这里首先执行一条 x87 指令,内存 [mem16int] 的位置在哪无关紧要,在这个例子中并不影响指令的执行效果。指令执行目的是获得 last instruction pointer 和 last data pointer,接着使用 fstenv 指令来保存 x87 Environment,值得注意的是:这里使用的是 16 位的内存模式。
我们可以使用 bochs 的调试功能,使用 fp 命令来输出 float point 寄存器
<bochs:7> fp
status  word: 0x82c1: B c3 TOS0 c2 C1 c0 ES SF pe ue oe ze de IE
control word: 0x0040: inf RC_NEAREST PC_32 pm um om zm dm im
tag word:     0x5555
operand:      0x0706
fip:          0x0000000000006c28
fcs:          0x0100
fdp:          0x0000000000007c45
fds:          0x1000
上面命令输出:
  • fip(last instruction pointer)为 0x6c28 实际上就是 0x6c28 + 0x1000 = 0x7c28
  • fcs(CS segment)为 0x0100
  • fdp(last data pointer)为 0x7c45 实际上就是 0x7c45 + 0x10000 = 0x17c45
  • fds(DS segment)为 0x1000
然后,我们观察 fstenv 指令执行结果,验证上面是否正确,我们可以打印 state 内存点的值:
(0) [0x0000000000007c2d] 0100:6c2d (unk. ctxt): fnstenv cs:0x6c47         ; 2ed936476c
<bochs:10> x /7h 0x1000+0x6c47
[bochs]:
0x0000000000007c47 <bogus+       0>: 0x0040 0x82c1 0x5555 0x7c28 0x0706 0x7c45 0x1000
fnstenv 指令在 cs:0x6c47(即:0x7c47) 的位置上保存 x87 Environment,我们验证得到:
  • [0x7c47] = 0x0040 这是 control word
  • [0x7c49] = 0x82c1 这是 status word
  • [0x7c51] = 0x5555 这是 tag word
接下来 20 位是 last instruction pointer 被拆分两部分,高 4 位在下一个字节的高 4 位,结果 last instruction pointer 是 0x7c28,接下来是 11 位的 last opcode 为:
  • 0x0706 = 0000 0111 0000 0110
真实的 x87 opcode 是 d8 + 7 06 = df 06,接下来是 last data pointer 组成原理与 last instruction 是一样的,为 0x17c45
本例的源码和映像下像:x87environment_demo1.zip
下图是在 bochs 2.4.6 下运行的截图:

上图中打印出了 last data pointer 和 last instrcution pointer,以及根据 last instrcution pointer 获取的 x87 instruction encodes
这里有完整的源代码以及映像下载:x87environment_demo_real16.zip

2.1.2 real mode 下的 32 位内存模式下面是 x87 Environment 在 real mode 的 32 位内存模式下的布局:

在 real mode 下缺省的操作数大小是 16 位的,在缺省情况下使用指令 fstenv/fnstenv 可以获得 16 位的 state image,为了获得 32 位的 state image,我在代码里修改如下:
        db 0x66                                       ; override to 32-bit
        fnstenv [cs: (state - 0x1000)]                ; CS:stat = 0x0100:XXXX
我手工加上 operand-size override prefix 将操作数调整为 32 位,运行结果如下:

上面是在 bochs 中的运行结果,得到的是 32 位的 real mode 的 state image,我们可以看出,图上红色标记的是分别是:
  • control word
  • status word
  • tag word
  • last instruction pointer
  • last data pointer
这些寄存器的高 16 位,在 32 位的的 state image 中这些高 16 位被写入 0xFFFF 值。对应在 intel 给出的图中的灰色部分。
继承事业,薪火相传
返回列表