In the early 1980s, floating-point arithmetic was fragmented by incompatible standards and hardware designs prioritizing simplicity over accuracy. Intel's 8087 floating-point coprocessor, introduced in 1980, aimed to address these issues by providing precise and reliable floating-point operations. It significantly accelerated calculations on IBM PCs and helped establish a widely adopted floating-point standard.
The 8087 executes its instructions through complex microcode stored in an internal ROM. A team known as the Opcode Collective has been reverse-engineering this microcode to better understand the chip's inner workings. Their recent focus has been on the FSCALE instruction, which scales a floating-point number by a power of two more efficiently than a full multiplication.
Contrary to expectations, FSCALE is not a simple instruction. Its microcode comprises over 140 micro-instructions and involves three levels of subroutine calls to manage numerous special cases, including zero, infinity, denormalized numbers, and Not-a-Number (NaN) values.
The 8087 organizes floating-point values in an eight-register stack, each holding an 80-bit number with a sign bit, a 15-bit biased exponent, and a 64-bit significand. Each register also has a tag indicating whether it holds a valid number, zero, special value, or is empty. The chip uses temporary registers for intermediate calculations.
FSCALE works by adding the integer value of the scale argument to the exponent of the number to be scaled. The microcode first handles trivial cases where either argument is zero, then converts the scale argument from floating-point to integer by shifting the significand appropriately. It then adds or subtracts this integer to the exponent, checking for overflow or underflow.
Special values and exceptions are handled by dedicated micro-subroutines. For example, if either argument is NaN, the microcode compares the two NaNs and returns the larger one, a feature intended to help programmers diagnose errors.
When scaling results in overflow or underflow, the 8087 triggers exceptions or interrupts depending on the control register settings. The chip supports multiple rounding modes and can produce denormalized numbers, which extend the range of representable values below the smallest normal number by relaxing the leading bit rule in the significand.
Creating denormalized numbers involves shifting the significand and carefully computing rounding bits—guard, round, and sticky bits—to maintain accuracy. The 8087's microcode includes a subroutine to adjust the precision of results according to the desired floating-point format and rounding mode.
This detailed microcode analysis highlights the complexity of the 8087's design, which balances mathematical rigor with hardware constraints. The chip's sophisticated handling of floating-point corner cases laid the groundwork for the IEEE 754 standard used in modern computing.
The Opcode Collective continues to study the 8087 microcode, providing insights into early floating-point hardware design and its influence on contemporary processors.