Introduction
The x87 instructions are the original x86 floating-point instructions. They first
ran on a separate chip, a coprocessor beside the main processor: the 8087 (1980) for
the 8086 and 8088, the 80287 (1982) for the 80286 and the 80387 (1987) for the 80386.
The 80387 was the first to follow the IEEE 754 standard in full, and added
fsin, fcos and the unordered comparisons. From the 80486DX
(1989) on, the unit is built into the processor.
Every x87 instruction starts with one of the eight escape bytes D8 to DF. The ModRM byte after it says the rest: with a memory operand, ModRM.reg picks the operation; with ModRM.mod 11, the whole byte does, and ModRM.r/m names a register, st(i).
A program checks for the unit with CPUID leaf 1: EDX bit 0. On 64-bit systems SSE2 has taken over most scalar floating point, but the x87 instructions still work in every mode, and the x87 unit is the only one that computes in 80-bit extended precision.
The register stack
The unit has eight 80-bit data registers, R0 to R7. Instructions do not name them
directly: they are used as a stack. A 3-bit field of the status word, TOP, says which
register is the top of the stack, st(0); st(i) is register
R[(TOP + i) mod 8]. A load pushes (TOP goes down by one and the value goes into
the new st(0)); a store with a p at the end of its name pops (the
register is marked empty and TOP goes up by one).
Each register holds a value in double extended precision: a sign bit, a 15-bit exponent and a 64-bit significand whose integer bit is stored, not implied. Loads convert single precision (32 bits), double precision (64 bits), integers of 16, 32 and 64 bits and 18-digit packed BCD into this form, and stores convert back.
The eight MMX registers are the low 64 bits of
R0 to R7, so MMX code and x87 code cannot be mixed without emms.
The control, status and tag words
Control word
Set with fldcw, read with fnstcw. fninit
sets it to 037Fh: every exception masked, 64-bit precision, round to nearest.
| Bits | Name | Meaning |
|---|---|---|
| 0 | IM | Invalid-operation exception mask |
| 1 | DM | Denormal-operand exception mask |
| 2 | ZM | Divide-by-zero exception mask |
| 3 | OM | Overflow exception mask |
| 4 | UM | Underflow exception mask |
| 5 | PM | Precision (inexact result) exception mask |
| 6 | – | Reserved |
| 7 | IEM | 8087 only: interrupt enable mask, set by fndisi and cleared by fneni. Reserved from the 80287 on. |
| 9:8 | PC | Precision control, for the significand of results: 00 single (24 bits), 01 reserved, 10 double (53 bits), 11 extended (64 bits) |
| 11:10 | RC | Rounding control: 00 to nearest (even), 01 down, 10 up, 11 towards zero |
| 12 | X | Infinity control: projective (0) or affine (1) infinity on the 8087 and 80287. Ignored from the 80387 on, which is always affine. |
| 15:13 | – | Reserved |
A masked exception gives a default result, such as a NaN or an infinity. An
unmasked one is reported when the next x87 instruction (or wait) runs:
as #MF, interrupt 16, when CR0.NE is set, and otherwise through the old external
FERR# signal.
Status word
Read with fnstsw, into memory or AX.
| Bits | Name | Meaning |
|---|---|---|
| 0 | IE | Invalid operation occurred |
| 1 | DE | Denormal operand occurred |
| 2 | ZE | Divide by zero occurred |
| 3 | OE | Overflow occurred |
| 4 | UE | Underflow occurred |
| 5 | PE | Precision (inexact result) occurred |
| 6 | SF | Stack fault: the invalid operation was a stack overflow (C1 = 1) or underflow (C1 = 0). From the 80387 on. |
| 7 | ES | Error summary: an unmasked exception is pending |
| 8 | C0 | Condition code bit 0 |
| 9 | C1 | Condition code bit 1; also whether a result was rounded up |
| 10 | C2 | Condition code bit 2; also “incomplete” for fprem and “out of range” for fsin and the rest |
| 13:11 | TOP | Which register is st(0) |
| 14 | C3 | Condition code bit 3 |
| 15 | B | Busy (kept for the 8087, where it showed the unit was working) |
The exception flags are sticky: they stay set until fnclex or
fninit. A comparison sets C3, C2 and C0 as below; fnstsw ax
then sahf copies them into ZF, PF and CF, where a conditional jump
reads them. fcomi and the other Pentium Pro comparisons set ZF, PF and
CF directly.
| Result | C3 (ZF) | C2 (PF) | C0 (CF) |
|---|---|---|---|
| st(0) > source | 0 | 0 | 0 |
| st(0) < source | 0 | 0 | 1 |
| st(0) = source | 1 | 0 | 0 |
| Unordered (a NaN) | 1 | 1 | 1 |
Tag word
Two bits for each physical register, R0 in bits 1:0 up to R7 in bits 15:14. It is
saved and loaded with the environment (fnstenv, fldenv).
fninit sets it to FFFFh, every register empty.
| Tag | Meaning |
|---|---|
| 00 | Valid: a normal finite value |
| 01 | Zero |
| 10 | Special: NaN, infinity, denormal or an unsupported format |
| 11 | Empty |
Pushing onto a register that is not empty is a stack overflow; reading an empty one
is a stack underflow. Both are invalid-operation exceptions with SF set. The
fxsave instruction stores an abridged tag word, one bit per register
(1 = not empty).
The environment
Besides the three words, the unit keeps the address of the last x87 instruction,
its opcode (11 bits) and the address of its memory operand, for exception handlers.
fnstenv stores all of this as the 14-byte (16-bit operand size) or
28-byte (32-bit) environment; fnsave stores the environment and the
eight registers, 94 or 108 bytes, and then reinitialises the unit.
The instructions
In the forms, m32fp, m64fp and m80fp are
floating-point values in memory, m16int, m32int and
m64int integers, and st(i) a register of the stack.
/n is ModRM.reg with a memory operand; C0+i is a ModRM byte
with mod 11, where i is ModRM.r/m. Where an instruction has no operand, it
works on st(0) and, if it needs one, st(1).
Loads, stores and moves
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
fld | m32fpm64fpm80fpst(i) | D9 /0DD /0DB /5D9 C0+i | Pushes a floating-point value from memory, or a copy of st(i). | 8087 (Intel 8087, 1980) |
fst | m32fpm64fpst(i) | D9 /2DD /2DD D0+i | Stores st(0), rounded to the destination’s precision, without popping. | 8087 (Intel 8087, 1980) |
fstp | m32fpm64fpm80fpst(i) | D9 /3DD /3DB /7DD D8+i | Stores st(0) and pops. Only this form can store 80 bits. | 8087 (Intel 8087, 1980) |
fild | m16intm32intm64int | DF /0DB /0DF /5 | Pushes a signed integer from memory, converted exactly. | 8087 (Intel 8087, 1980) |
fist | m16intm32int | DF /2DB /2 | Stores st(0) as a signed integer, rounded by the control word’s RC. Out of range, it stores the “integer indefinite” (the most negative value) and signals invalid operation. | 8087 (Intel 8087, 1980) |
fistp | m16intm32intm64int | DF /3DB /3DF /7 | The same, then pops. Only this form can store 64 bits. | 8087 (Intel 8087, 1980) |
fisttp | m16intm32intm64int | DF /1DB /1DD /1 | Stores st(0) as a signed integer truncated towards zero, whatever RC says, then pops. This is the conversion C needs, without changing the control word. CPUID leaf 1: ECX bit 0. | SSE3 (Intel Pentium 4 (Prescott), 2004) |
fbld | m80bcd | DF /4 | Pushes an 18-digit packed BCD integer (9 bytes of digits and a sign byte). | 8087 (Intel 8087, 1980) |
fbstp | m80bcd | DF /6 | Stores st(0), rounded to an integer, as packed BCD, then pops. | 8087 (Intel 8087, 1980) |
fxch | st(i) | D9 C8+i | Exchanges st(0) and st(i); with no operand, st(1). | 8087 (Intel 8087, 1980) |
fcmovb | st(0), st(i) | DA C0+i | Copies st(i) to st(0) if below (CF = 1). CPUID leaf 1: EDX bit 15 (CMOV) with bit 0. | Pentium Pro (Intel Pentium Pro, 1995) |
fcmove | st(0), st(i) | DA C8+i | Copies if equal (ZF = 1). | Pentium Pro (Intel Pentium Pro, 1995) |
fcmovbe | st(0), st(i) | DA D0+i | Copies if below or equal (CF = 1 or ZF = 1). | Pentium Pro (Intel Pentium Pro, 1995) |
fcmovu | st(0), st(i) | DA D8+i | Copies if unordered (PF = 1). | Pentium Pro (Intel Pentium Pro, 1995) |
fcmovnb | st(0), st(i) | DB C0+i | Copies if not below (CF = 0). | Pentium Pro (Intel Pentium Pro, 1995) |
fcmovne | st(0), st(i) | DB C8+i | Copies if not equal (ZF = 0). | Pentium Pro (Intel Pentium Pro, 1995) |
fcmovnbe | st(0), st(i) | DB D0+i | Copies if not below or equal (CF = 0 and ZF = 0). | Pentium Pro (Intel Pentium Pro, 1995) |
fcmovnu | st(0), st(i) | DB D8+i | Copies if not unordered (PF = 0). | Pentium Pro (Intel Pentium Pro, 1995) |
Constants
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
fldz | – | D9 EE | Pushes +0.0. | 8087 (Intel 8087, 1980) |
fld1 | – | D9 E8 | Pushes +1.0. | 8087 (Intel 8087, 1980) |
fldpi | – | D9 EB | Pushes π. | 8087 (Intel 8087, 1980) |
fldl2t | – | D9 E9 | Pushes log₂ 10. | 8087 (Intel 8087, 1980) |
fldl2e | – | D9 EA | Pushes log₂ e. | 8087 (Intel 8087, 1980) |
fldlg2 | – | D9 EC | Pushes log₁₀ 2. | 8087 (Intel 8087, 1980) |
fldln2 | – | D9 ED | Pushes logₑ 2. | 8087 (Intel 8087, 1980) |
Arithmetic
Each operation has a memory form (st(0) op memory), two register forms
(st(0) = st(0) op st(i), and st(i) = st(i) op st(0)), a popping form
(p: st(i) = st(i) op st(0), then pop; with no operand, st(1)) and an
integer form (fi). The r forms reverse the operands of
subtraction and division.
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
fadd | m32fpm64fpst(0), st(i)st(i), st(0) | D8 /0DC /0D8 C0+iDC C0+i | Adds. | 8087 (Intel 8087, 1980) |
faddp | st(i), st(0) | DE C0+i | Adds st(0) to st(i), then pops. | 8087 (Intel 8087, 1980) |
fiadd | m32intm16int | DA /0DE /0 | Adds an integer from memory to st(0). | 8087 (Intel 8087, 1980) |
fsub | m32fpm64fpst(0), st(i)st(i), st(0) | D8 /4DC /4D8 E0+iDC E8+i | Subtracts the second operand from the first. | 8087 (Intel 8087, 1980) |
fsubp | st(i), st(0) | DE E8+i | st(i) = st(i) − st(0), then pops. | 8087 (Intel 8087, 1980) |
fisub | m32intm16int | DA /4DE /4 | st(0) = st(0) − integer. | 8087 (Intel 8087, 1980) |
fsubr | m32fpm64fpst(0), st(i)st(i), st(0) | D8 /5DC /5D8 E8+iDC E0+i | Reverse subtraction: the first operand from the second, into the first. | 8087 (Intel 8087, 1980) |
fsubrp | st(i), st(0) | DE E0+i | st(i) = st(0) − st(i), then pops. | 8087 (Intel 8087, 1980) |
fisubr | m32intm16int | DA /5DE /5 | st(0) = integer − st(0). | 8087 (Intel 8087, 1980) |
fmul | m32fpm64fpst(0), st(i)st(i), st(0) | D8 /1DC /1D8 C8+iDC C8+i | Multiplies. | 8087 (Intel 8087, 1980) |
fmulp | st(i), st(0) | DE C8+i | Multiplies st(i) by st(0), then pops. | 8087 (Intel 8087, 1980) |
fimul | m32intm16int | DA /1DE /1 | Multiplies st(0) by an integer. | 8087 (Intel 8087, 1980) |
fdiv | m32fpm64fpst(0), st(i)st(i), st(0) | D8 /6DC /6D8 F0+iDC F8+i | Divides the first operand by the second. | 8087 (Intel 8087, 1980) |
fdivp | st(i), st(0) | DE F8+i | st(i) = st(i) ÷ st(0), then pops. | 8087 (Intel 8087, 1980) |
fidiv | m32intm16int | DA /6DE /6 | st(0) = st(0) ÷ integer. | 8087 (Intel 8087, 1980) |
fdivr | m32fpm64fpst(0), st(i)st(i), st(0) | D8 /7DC /7D8 F8+iDC F0+i | Reverse division: the second operand by the first, into the first. | 8087 (Intel 8087, 1980) |
fdivrp | st(i), st(0) | DE F0+i | st(i) = st(0) ÷ st(i), then pops. | 8087 (Intel 8087, 1980) |
fidivr | m32intm16int | DA /7DE /7 | st(0) = integer ÷ st(0). | 8087 (Intel 8087, 1980) |
fabs | – | D9 E1 | Clears the sign of st(0). | 8087 (Intel 8087, 1980) |
fchs | – | D9 E0 | Inverts the sign of st(0). | 8087 (Intel 8087, 1980) |
fsqrt | – | D9 FA | Square root of st(0). | 8087 (Intel 8087, 1980) |
frndint | – | D9 FC | Rounds st(0) to an integer, by RC. | 8087 (Intel 8087, 1980) |
fscale | – | D9 FD | Multiplies st(0) by 2 to the power of st(1), truncated to an integer. | 8087 (Intel 8087, 1980) |
fxtract | – | D9 F4 | Splits st(0) into its exponent and significand: the exponent replaces st(0), then the significand is pushed. | 8087 (Intel 8087, 1980) |
fprem | – | D9 F8 | Partial remainder of st(0) ÷ st(1), with the quotient truncated towards zero (as C’s fmod). It reduces the exponent by at most 63 each time: C2 = 1 means run it again. C0, C3 and C1 receive the low three bits of the quotient. | 8087 (Intel 8087, 1980) |
fprem1 | – | D9 F5 | The same with the quotient rounded to nearest, the IEEE 754 remainder. | 80387 (Intel 80387, 1987) |
Comparison and classification
The fcom forms raise invalid operation for any NaN; the
fucom forms (unordered) only for a signalling NaN.
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
fcom | m32fpm64fpst(i) | D8 /2DC /2D8 D0+i | Compares st(0) with the operand (st(1) with none) and sets C3, C2 and C0. | 8087 (Intel 8087, 1980) |
fcomp | m32fpm64fpst(i) | D8 /3DC /3D8 D8+i | Compares, then pops. | 8087 (Intel 8087, 1980) |
fcompp | – | DE D9 | Compares st(0) with st(1), then pops both. | 8087 (Intel 8087, 1980) |
ficom | m32intm16int | DA /2DE /2 | Compares st(0) with an integer. | 8087 (Intel 8087, 1980) |
ficomp | m32intm16int | DA /3DE /3 | Compares with an integer, then pops. | 8087 (Intel 8087, 1980) |
fucom | st(i) | DD E0+i | Unordered compare of st(0) with st(i). | 80387 (Intel 80387, 1987) |
fucomp | st(i) | DD E8+i | Unordered compare, then pops. | 80387 (Intel 80387, 1987) |
fucompp | – | DA E9 | Unordered compare of st(0) with st(1), then pops both. | 80387 (Intel 80387, 1987) |
fcomi | st(0), st(i) | DB F0+i | Compares and sets ZF, PF and CF directly (as C3, C2 and C0 would be), clearing OF, SF and AF. | Pentium Pro (Intel Pentium Pro, 1995) |
fcomip | st(0), st(i) | DF F0+i | The same, then pops. | Pentium Pro (Intel Pentium Pro, 1995) |
fucomi | st(0), st(i) | DB E8+i | Unordered compare into ZF, PF and CF. | Pentium Pro (Intel Pentium Pro, 1995) |
fucomip | st(0), st(i) | DF E8+i | The same, then pops. | Pentium Pro (Intel Pentium Pro, 1995) |
ftst | – | D9 E4 | Compares st(0) with 0.0. | 8087 (Intel 8087, 1980) |
fxam | – | D9 E5 | Classifies st(0) into C3, C2 and C0 (unsupported, NaN, normal, infinity, zero, empty or denormal), with its sign in C1. | 8087 (Intel 8087, 1980) |
Transcendental functions
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
fsin | – | D9 FE | Replaces st(0) with its sine (radians). For |x| ≥ 2⁶³ it sets C2 and leaves st(0) unchanged. | 80387 (Intel 80387, 1987) |
fcos | – | D9 FF | Replaces st(0) with its cosine. | 80387 (Intel 80387, 1987) |
fsincos | – | D9 FB | Replaces st(0) with its sine, then pushes its cosine. | 80387 (Intel 80387, 1987) |
fptan | – | D9 F2 | Replaces st(0) with its tangent, then pushes 1.0 (kept for the 8087, whose tangent was a ratio). | 8087 (Intel 8087, 1980) |
fpatan | – | D9 F3 | Arctangent of st(1) ÷ st(0), with the quadrant from both signs; the result replaces st(1), then pops. | 8087 (Intel 8087, 1980) |
f2xm1 | – | D9 F0 | Replaces st(0), which must be between −1 and +1, with 2 to its power, minus 1. | 8087 (Intel 8087, 1980) |
fyl2x | – | D9 F1 | st(1) × log₂ st(0); the result replaces st(1), then pops. | 8087 (Intel 8087, 1980) |
fyl2xp1 | – | D9 F9 | st(1) × log₂(st(0) + 1), more accurate for st(0) near 0; then pops. | 8087 (Intel 8087, 1980) |
Control and state
The names with fn (“no wait”) do not first check for a pending
exception. Assemblers write finit, fclex,
fstcw, fstsw, fstenv and fsave
for the same instruction after a wait (9B), which reports any pending
exception first. The disassembler shows the 9B as its own wait.
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
fninit | – | DB E3 | Initialises the unit: control word 037Fh, status word 0, tag word FFFFh, and the instruction and data pointers cleared. | 8087 (Intel 8087, 1980) |
fnclex | – | DB E2 | Clears the exception flags, SF, ES and B in the status word. | 8087 (Intel 8087, 1980) |
fldcw | m16 | D9 /5 | Loads the control word. | 8087 (Intel 8087, 1980) |
fnstcw | m16 | D9 /7 | Stores the control word. | 8087 (Intel 8087, 1980) |
fnstsw | m16AX | DD /7DF E0 | Stores the status word in memory, or (from the 80287 on) in AX. | 8087 (Intel 8087, 1980); AX form 80287 (Intel 80287, 1982) |
fnstenv | m14/m28 | D9 /6 | Stores the environment, then masks every exception. | 8087 (Intel 8087, 1980) |
fldenv | m14/m28 | D9 /4 | Loads the environment. | 8087 (Intel 8087, 1980) |
fnsave | m94/m108 | DD /6 | Stores the environment and the eight registers, then initialises the unit as fninit does. | 8087 (Intel 8087, 1980) |
frstor | m94/m108 | DD /4 | Loads the environment and the eight registers. | 8087 (Intel 8087, 1980) |
fincstp | – | D9 F7 | Adds 1 to TOP. Unlike a pop, it does not mark the register empty. | 8087 (Intel 8087, 1980) |
fdecstp | – | D9 F6 | Subtracts 1 from TOP. | 8087 (Intel 8087, 1980) |
ffree | st(i) | DD C0+i | Marks st(i) empty, without changing TOP. | 8087 (Intel 8087, 1980) |
fnop | – | D9 D0 | Does nothing, but is an x87 instruction: it updates the instruction pointer and can report a pending exception. | 8087 (Intel 8087, 1980) |
wait | – | 9B | Waits for the unit and reports any pending unmasked exception. Also written fwait. On the 8086 it waited for the 8087’s TEST input. | 8086 (Intel 8086, 1978), for the 8087 |
Undocumented aliases and obsolete instructions
Intel’s current opcode map leaves these ModRM bytes out, but processors still accept them. Most are aliases: the same operation as a documented instruction at another place in the map. The 8087 and 80287 instructions at the end do nothing on later processors. The disassembler shows them by LLVM’s names, so that a byte sequence that uses them, by accident or to confuse a disassembler, reads as what the processor does. For the aliases, the Set column gives the disassembler’s set: which processor first ran each one is not documented.
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
fcom2 | st(i) | DC D0+i | Alias of fcom st(i) (D8 D0+i). | 8087 (Intel 8087, 1980) |
fcomp3 | st(i) | DC D8+i | Alias of fcomp st(i) (D8 D8+i). | 8087 (Intel 8087, 1980) |
fcomp5 | st(i) | DE D0+i | Alias of fcomp st(i). | 8087 (Intel 8087, 1980) |
fxch4 | st(i) | DD C8+i | Alias of fxch st(i) (D9 C8+i). | 8087 (Intel 8087, 1980) |
fxch7 | st(i) | DF C8+i | Alias of fxch st(i). | 8087 (Intel 8087, 1980) |
fstp8 | st(i) | DF D0+i | Alias of fstp st(i) (DD D8+i). | 8087 (Intel 8087, 1980) |
fstp9 | st(i) | DF D8+i | Alias of fstp st(i). | 8087 (Intel 8087, 1980) |
fstpnce | st(i) | D9 D8+i | Stores st(0) into st(i) and pops, like fstp st(i); also called fstp1. sandpile.org notes that, unlike the plain aliases, it differs in how it signals a stack underflow. | 8087 (Intel 8087, 1980) |
ffreep | st(i) | DF C0+i | Marks st(i) empty, then pops: ffree and a pop in one. Not an alias. AMD’s optimisation guide for the Athlon recommended it for freeing the stack. | 8087 (Intel 8087, 1980) |
fneni | – | DB E0 | 8087 only: clears the interrupt enable mask (control word bit 7), so the 8087 may interrupt the processor. The 80287 and later do nothing (with a wait before it: feni). | 8087 (Intel 8087, 1980) |
fndisi | – | DB E1 | 8087 only: sets the interrupt enable mask, so the 8087 may not interrupt. The 80287 and later do nothing (fdisi with wait). | 8087 (Intel 8087, 1980) |
fnsetpm | – | DB E4 | 80287 only: tells the 80287 that the processor has switched to protected mode. The 80387 and later treat it as a no-operation. | 80287 (Intel 80287, 1982) |
Notes
- Names of the reversed forms. In the forms whose destination is
st(i) (DC E0+i to DC FF and DE E0+i to DE FF), AT&T syntax, as GNU and LLVM
write it, swaps
fsubwithfsubrandfdivwithfdivr, an old compatibility with the UNIX System V assembler. The disassembler uses Intel’s names, as the table above does. - Prefixes. The operand-size prefix (66) chooses the 14-byte or 28-byte environment and the 94-byte or 108-byte save area. REX.W has no effect on x87 instructions; their 64-bit forms are fixed by the opcode.
- Not decoded. DB E5, which only the 80287XL ran (as
frstpm), and the other empty places in the D9, DA, DB, DE and DF register forms are shown as invalid, as LLVM does. - State saving.
fxsaveandxsave, on the System instructions page, save the x87 state with the SSE state, and are what operating systems use today.
Links
Reference
- Intel 64 and IA-32 Architectures Software Developer’s Manuals – Intel’s manuals; volume 2 describes each instruction, volume 1 chapter 8 describes the x87 unit and its registers
- AMD64 Architecture Programmer’s Manual, Volume 5 – AMD’s reference to the 64-bit media and x87 instructions
- x86 architecture FPU opcodes – sandpile.org’s map of the x87 opcodes, with the aliases and the 8087 and 80287 instructions
- x86 and amd64 instruction reference – Félix Cloutier’s web copy of the instruction pages of Intel’s manual