汇编语言FWAIT(WAIT)指令:异常同步

整数 (CPU) 和 FPU 是相互独立的单元,因此,在执行整数和系统指令的同时可以执行浮点指令。这个功能被称为并行性 (concurrency),当发生未屏蔽的浮点异常时,它可能是个潜在的问题。反之,已屏蔽异常则不成问题,因为,FPU 总是可以完成当前操作并保存结果。

发生未屏蔽异常时,中断当前的浮点指令,FPU 发异常事件信号。当下一条浮点指令或 FWAIT(WAIT) 指令将要执行时,FPU 检查待处理的异常。如果发现有这样的异常,FPU 就调用浮点异常处理程序(子程序)。

如果引发异常的浮点指令后面跟的是整数或系统指令,情况又会是怎样的呢?很遗憾,指令不会检查待处理异常,它们会立即执行。假设第一条指令将其输出送入一个内存操作数,而第二条指令又要修改同一个内存操作数,那么异常处理程序就不能正确执行。示例如下:

.data
intVal DWORD 25
.code
fild intVal  ;将整数加载到 ST(0)
inc intVal  ;整数加 1

设置 WAIT 和 FWAIT 指令是为了在执行下一条指令之前,强制处理器检查待处理且未屏蔽的浮点异常。这两条指令中的任一条都可以解决这种潜在的同步问题,直到异常处理程序结束,才执行 INC 指令。

fild intVal  ;将整数加载到 ST(0)
fwait         ;等待待处理异常
inc intVal  ;整数加 1

下面将用几个简短的例子来演示浮点算术运算指令。一个很好的学习方法是用 C++ 编写表达式,编译后,再检查由编译器生成的代码。

表达式

现在编写代码,计算表达式 valD=-valA+(valB*valC)。下面给出一种可能的循序渐进的方法:将 valA 加载到堆栈,并取其负数;将 valB 加载到 ST(0),则 valA 成为 ST(1);将 ST(0) 和 valC 相乘,乘积保存在 ST(0) 中;将 ST(1) 与 ST(0) 相加,和数保存到 valD:
.data
valA REAL8 1.5
valB REAL8 2.5
valC REAL8 3.0
valD REAL8 ?; +6.0
.code
fld valA    ; ST(0) = valA
fchs        ;修改 ST(0) 的符号
fld valB    ; 将 valB 加载到 ST(0)
fmul valC   ; ST(0) *= valC
fadd        ; ST(0) += ST(1)
fstp valD   ; 将 ST(0) 保存到 valD

数组求和

下面的代码计算并显示一个双精度实数数组之和:
ARRAY_SIZE = 20
.data
sngArray REAL8 ARRAY_SIZE DUP(?)
.code
    mov    esi, 0            ;数组索引
    fldz                     ; 0.0 入栈
    mov ecx,ARRAY_SIZE
L1: fld    sngArray[esi]     ;将内存操作数加载到ST(0)
    fadd                     ; ST(0) 加 ST(1),出栈
    add esi,TYPE REAL8       ;移至!I 下一个元素
    loop L1
    call WriteFloat          ;显示 ST(0) 中的和数

平方根之和

FSQRT 指令对 ST(0) 中的数值求平方根,并将结果送回 ST(0)。下面的代码计算了两个数的平方根之和:
.data
valA REAL8 25.0
valB REAL8 36.0
.code
fid valA        ; valA 入栈
fsqrt           ; ST(0) = sqrt(valA)
fid valB        ; valB 入栈
fsqrt           ; ST(0) = sqrt(valB)
fadd            ; ST (0)+ST(1)

数组点积

下面的代码计算了表达式 (airay[0]*airay[l]) + (array[2]*array[3])。该计算有时也被称为点积 (dot product)。

.data
array REAL4 6.0, 2.0, 4.5, 3.2

下表列出了每条指令执行后,FPU 堆栈的情况。输入数据如下:

指令  FPU堆栈 指令 FPU堆栈
fld array ST(0):+6.0000000E+000 fmul [array+12] ST(0):+1.4400000E+001
fmul [array+4] ST(0):+1.2000000E+001   ST(1):+1.2000000E+001
fld [array+8] ST(0):+4.5000000E+000 fadd ST(0):+2.6400000E+001
  ST(1):+1.2000000E+001