A few weeks ago I had the questionable pleasure of diving into the math exception handler of WIN87EM.DLL, the Windows 3.1 math emulator and FPU support library. Actually WIN87EM.DLL appears to have been first shipped with Windows 3.0, and the version in Windows 3.1 is more or less identical. The version shipped with Windows 3.11 is byte for byte identical to the one in Windows 3.1.
The main function of WIN87EM.DLL is, as the name suggests, an x87 floating-point emulator. It appears to be an outgrowth of the math emulation packages shipped with many Microsoft languages. But even on a system with x87 hardware, WIN87EM.DLL has some work to do.
Namely WIN87EM.DLL intercepts math errors (exceptions). Depending on the system type, WIN87EM.DLL hooks either NMI vector 2 (PC and PC/XT) or IRQ 13 (PC/AT and compatibles).
The math interrupt handler in WIN87EM.DLL is very, very strange. It bears all the hallmarks of code that was written, rewritten, rewritten again, hacked, tweaked, modified, and eventually beaten into submission even if the author(s) had no real idea why it finally worked.
Now, x87 math error handing is a very tricky subject where numerous details changed over FPU and CPU generations, and Intel had a fair share of bugs in this area. But the code in WIN87EM.DLL looks very much like the result of changes made in desperation until it worked somehow, even though the changes made little or no sense.
What WIN87EM.DLL Does
The IRQ 13 handler in WIN87EM.DLL performs the following steps:
- Disable interrupts (CLI)
- PUSH and POP the AX register 70 times, maybe it will slow things down
- Write zero to I/O port F0h to clear the PC/AT FPU error latch
- Mask a selection of interrupts
- Write an EOI to the master interrupt controller (but not slave)
- Execute the FNSTSW instruction to store the FPU status word (but only that)
- PUSH and POP the AX register 16 times, because speed kills
- Write zero to I/O port F0h again, in case it didn’t work the first time
- Execute the FNCLEX instruction to clear pending FPU exceptions
- Write zero to I/O port F0h again, because third time’s the charm
- PUSH and POP the AX register 16 times, because it was so much fun last time
- Execute the FNCLEX instruction again, just to be really sure
- PUSH and POP the AX register 16 times, because it’s the thing to do
- Write zero to I/O port F0h again, because three times might not have been enough
- Finally jump to code that doesn’t look crazy
The not so crazy looking code executes FNSTENV to store the FPU environment, swaps in the previously saved status word (which was subsequently changed by FNCLEX), examines the code at the stored FPU error pointer to see if instruction prefixes should be skipped (that is where things may crash), enables interrupts with STI, and finally jumps to common code that’s executed on both PCs and ATs and handles the actual math error.
All in all, the math error interrupt handler in WIN87EM.DLL makes very little sense. I am extremely doubtful that, for example, writing to I/O port F0h four times does anything useful, or that executing FNCLEX twice is better than doing it once. However, it is entirely possible that the extra time it takes might do something. Likewise the slowdown loops that push and pop AX are very unlikely to be necessary, but may have done something seemingly useful on some particular system.
The code really does look like a desperate attempt to make things work, and there is some possibility that it is the result of trying to support broken hardware. Other similar error handlers I looked at don’t appear nearly as convoluted and are much more “by the book”.
We’ll probably never know why the code ended up so crazy, but we can speculate.