This post ended up being much longer than originally intended because halfway into writing it, I found that 286 and later CPUs don’t behave the way I had assumed they would…
While investigating a bug related to a program using floating-point math on a 386SX system with no FPU, I started pondering how exactly FPU detection works on 286 and newer CPUs. Although math co-processors became standard some 30 years ago, on old PCs they were an uncommon and expensive add-on, and a 66 MHz 486SX2 would still have a usable yet FPU-less processor in the mid-1990s.
The CPU/FPU interface and FPU detection on the 8086/8088 was discussed before. To recap, the 8086/8087 interface is a little odd because it is in fact a generic co-processor interface. The 8086 was launched in 1978; probably sometime in 1979, the Intel 8089 I/O Coprocessor arrived; the 8087 only appeared in 1980.
The ESC instruction (opcode range D8h-DFh) was used for communication with a co-processor on the 8086. While the CPU didn’t exactly execute the instruction, it had to know how to decode it. The ESC instruction used a standard ModR/M byte to indicate an optional memory operand, which the CPU needed to be able to write to or read from the co-processor.
If there is no co-processor attached to an 8086, the ESC instructions simply do nothing because the co-processor isn’t there to read or write any data. However, the WAIT instruction designed for synchronization will (in a typical 8088/8086 PC design) hang indefinitely because the missing co-processor acts as if it were permanently busy. For that reason, FPU detection must use the non-waiting FNINIT/FNSTSW sequence (or an equivalent) to avoid hangs on 8086-class machines.
Additional information about what things look like from the 8087’s perspective has been recently published.
As an aside, it should be noted that the IBM PC had a mechanism to report FPU presence (or absence) in the BIOS equipment word (INT 11H). However, this detection relied entirely on a user-settable DIP switch on the PC and PC/XT motherboard. An article in PC Tech Journal in June 1985 (Machine Specifics, by Ted Forgeron) notes that IBM’s own manual gave users incorrect instructions, telling them that the DIP switch needed to be in the ON position to signal FPU presence. In reality, the DIP switch setting to report FPU presence was OFF. As a consequence, the BIOS FPU presence bit could not be trusted in PC and XT systems (on the PC/AT, BIOS detected FPU presence during POST) and software needed to explicitly check for FPU presence to be certain.
By the time the 80286 was rolled out in 1982, Intel had effectively given up on a generic co-processor interface (no co-processor other than the 80287 is known). Although Intel’s 286 documentation mentions the ESC instruction here and there, it is not listed in the instruction reference at all (unlike WAIT). ESC is only indirectly documented in the 80287 programming reference. The situation was the same with the 80386/80387 documentation; no ESC in the 386, only FPU instructions in the 387, according to Intel.
Unlike the 8086, the 286 and later had a convenient ability to simplify floating-point instruction emulation (a non-trivial topic on the 8086). The EM bit in the Machine Status Word (MSW, later the low 16 bits of CR0 register), when set, causes the ESC opcode to trigger a Coprocessor Not Available fault (exception 7).
Intel’s idea clearly was that on systems with no FPU, the EM bit should be set. Which is all well and good, except firmware or operating system still needed to figure out how to set the EM bit based on FPU presence or absence. The hardware itself offered no aid to detect an FPU; it had to be done in software. And to detect whether an FPU was present, Intel suggested executing an FNINIT/FNSTSW or similar sequence.
... continue reading