13.1.2 Windows 80x87 Floating-Point Emulation

With Windows 80x87 floating-point emulation, the Windows application contains calls to floating-point instructions for all floating-point operations, but the application also includes fixup records for each instruction. When Windows loads the application, Windows determines whether floating-point hardware is present. If the hardware it is not present, Windows uses the fixup records to replace the actual instructions with calls to emulation code.

To support this method, the application's startup routine must check whether WIN87EM.DLL is present. Then the routine must initialize WIN87EM.DLL by calling the __fpmath function with the BX register set to 0 and must set the floating-point exception handler by calling the __fpmath function with the BX register set to 3 and the DS:AX registers pointing to the exception handler. When the application's WinMain function returns to the startup routine, the routine must release WIN87EM.DLL by calling the __fpmath function with the BX register set to 2. After WIN87EM.DLL has been released, the startup routine can end the application.

For this method to work correctly, the Windows application must contain the proper fixup records—sometimes called operating system (OS) fixups—to convert instructions to emulation calls. For WIN87EM.DLL, each call consists of an interrupt (int) instruction followed by one or more words defining the floating-point operation and operands. The call is actually generated by the addition of fixup values to the first two words of the corresponding floating-point instruction. The fixup values to use depend on the instruction—the values are defined as follows:

fINT    equ     0CDh
fFWAIT  equ     09Bh
fESCAPE equ     0D8h
fFNOP   equ     090h
fES     equ     026h
fCS     equ     02Eh
fSS     equ     036h
fDS     equ     03Eh
BEGINT  equ     034h




FIARQQ  equ     (fINT + 256*(BEGINT + 8)) - (fFWAIT + 256*fDS)
FISRQQ  equ     (fINT + 256*(BEGINT + 8)) - (fFWAIT + 256*fSS)
FICRQQ  equ     (fINT + 256*(BEGINT + 8)) - (fFWAIT + 256*fCS)
FIERQQ  equ     (fINT + 256*(BEGINT + 8)) - (fFWAIT + 256*fES)
FIDRQQ  equ     (fINT + 256*(BEGINT + 0)) - (fFWAIT + 256*fESCAPE)
FIWRQQ  equ     (fINT + 256*(BEGINT + 9)) - (fFNOP  + 256*fFWAIT)
FJARQQ  equ     256*(((0 shl 6) or (fESCAPE and 03Fh)) - fESCAPE)
FJSRQQ  equ     256*(((1 shl 6) or (fESCAPE and 03Fh)) - fESCAPE)
FJCRQQ  equ     256*(((2 shl 6) or (fESCAPE and 03Fh)) - fESCAPE)

Each of the six fixup record types consists of two one-word values, as shown in the following example:

osfixuptbl  label word
    DW  FIARQQ, FJARQQ
    DW  FISRQQ, FJSRQQ
    DW  FICRQQ, FJCRQQ
    DW  FIERQQ, 0h
    DW  FIDRQQ, 0h
    DW  FIWRQQ, 0h
osfixuptbllen = $-osfixuptbl

The loader assumes that each floating-point instruction is preceded by a wait instruction. The loader adds the first word to the combination of the wait instruction byte and the first byte in the floating-point instruction. For fixup types 1 through 3, the loader adds the second word to the second and third bytes of the floating-point instruction. For types 4 through 6, the loader makes no changes to these bytes (it adds zero).

Because WIN87EM.DLL polls for exceptions by using the fwait instruction, the loader must replace each nop and fwait instruction pair with a call to emulation code, even if a floating-point coprocessor is available. These instructions must have a corresponding fixup record of type 6.

WIN87EM.DLL does not emulate the following floating-point instructions:

fbld fbstp fcos fdecstp fincstp finit fldenv fnop fprem1 frstor fsave fsetpm fsin fsincos fstenv fucom fucomp fucompp fxtract