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 |