The 8087 family of coprocessors has separate instructions for each of the following operations:
Loading and storing data
Doing arithmetic calculations
Controlling program flow
The following sections explain the available instructions and show how to use them for each of the operations listed above. See Section 6.2.2, “Instruction and Operand Formats,” for general syntax information.
Data-transfer instructions transfer data between main memory and the coprocessor registers or between different coprocessor registers. Two principles govern data transfers:
The choice of instruction determines whether a value in memory is considered an integer, a BCD number, or a real number. The value is always considered a 10-byte real number once it is transferred to the coprocessor.
The size of the operand determines the size of a value in memory. Values in the coprocessor always take up 10 bytes.
Summary: Load commands transfer data, and store commands remove data.
You can transfer data to stack registers using load commands. These commands push data onto the stack from memory or from coprocessor registers. Store commands remove data. Some store commands pop data off the register stack into memory or coprocessor registers; others simply copy the data without changing it on the stack.
If you use constants as operands, you cannot load them directly into coprocessor registers. You must allocate memory and initialize a variable to a constant value. That variable can then be loaded by using one of the load instructions listed below.
A few special instructions are provided for loading certain constants. You can load 0, 1, pi, and several common logarithmic values directly. Using these instructions is faster and often more precise than loading the values from initialized variables.
All instructions that load constants have the stack top as the implied destination operand. The constant to be loaded is the implied source operand.
The coprocessor data area, or parts of it, can also be moved to memory and later loaded back. You may want to do this to save the current state of the coprocessor before executing a procedure. After the procedure ends, restore the previous status. Saving coprocessor data is also useful when you want to modify coprocessor behavior by writing certain data to main memory, operating on the data with 8086-family instructions, and then loading it back to the coprocessor data area.
You can use the following instructions for transferring numbers to and from registers:
Instruction(s) | Description |
FLD, FST, FSTP | Loads and stores real numbers | |
FILD, FIST, FISTP | Loads and stores binary integers | |
FBLD | Loads BCD | |
FBSTP | Stores BCD | |
FXCH | Exchanges register values | |
FLDZ | Pushes 0 into ST | |
FLD1 | Pushes 1 into ST | |
FLDPI | Pushes the value of pi into ST | |
FLDCW mem2byte | Loads the control word into the coprocessor | |
F[[N]]STCW mem2byte, Stores the control word in memory | ||
FLDENV mem14byte | Loads environment from memory | |
F[[N]]STENV mem14byte | Stores environment in memory | |
FRSTOR mem94byte | Restores state from memory | |
F[[N]]SAVE mem94byte, Saves state in memory | ||
FLDL2E | Pushes the value of log2e into ST | |
FLDL2T | Pushes log210 into ST | |
FLDLG2 | Pushes log102 into ST | |
FLDLN2 | Pushes loge2 into ST |
The following example and Figure 6.7 illustrate some of these instructions:
.DATA
m1 REAL4 1.0
m2 REAL4 2.0
.CODE
fld m1 ; Push m1 into first item
fld st(2) ; Push third item into first
fst m2 ; Copy first item to m2
fxch st(2) ; Exchange first and third items
fstp m1 ; Pop first item into m1
Most of the coprocessor instructions for doing arithmetic operations have several forms, depending on the operand used. You do not need to specify the operand type in the instruction if both operands are stack registers, since register values are always 10-byte real numbers. The arithmetic instructions are listed below. In most cases, the result replaces the destination register.
Instruction | Description |
FADD | Adds the source and destination |
FSUB | Subtracts the source from the destination |
FSUBR | Subtracts the destination from the source |
FMUL | Multiplies the source and the destination |
FDIV | Divides the destination by the source |
FDIVR | Divides the source by the destination |
FABS | Sets the sign of ST to positive |
FCHS | Reverses the sign of ST |
FRNDINT | Rounds ST to an integer |
FSQRT | Replaces the contents of ST with its square root |
FSCALE | Multiplies the stack-top value by 2 to the power contained in ST(1) |
FPREM | Calculates the remainder of ST divided by ST(1) |
80387 Only
Instruction | Description |
FSIN | Calculates the sine of the value in ST | |
FCOS | Calculates the cosine of the value in ST | |
FSINCOS | Calculates the sine and cosine of the value in ST | |
FPREM1 | Calculates the partial remainder by performing modulo division on the top two stack registers | |
FXTRACT | Breaks a number down into its exponent and mantissa and pushes the mantissa onto the register stack | |
F2XM1 | Calculates 2x–1 | |
FYL2X | Calculates Y * log2 X | |
FYL2XP1 | Calculates Y * log2 (X+1) | |
FPTAN | Calculates the tangent of the value in ST | |
FPATAN | Calculates the arctangent of the ratio Y/X | |
F[[N]]INIT, Resets the coprocessor and restores all the default conditions in the control and status words | ||
F[[N]]CLEX, Clears all exception flags and the busy flag of the status word | ||
FINCSTP | Adds 1 to the stack pointer in the status word | |
FDECSTP | Subtracts 1 from the stack pointer in the status word | |
FFREE | Marks the specified register as empty |
The following example illustrating several arithmetic instructions solves quadratic equations. It does no error checking and fails for some values because it attempts to find the square root of a negative number. You could revise the code using the FTST (Test for Zero) instruction to check for a negative number or 0 before the square root is calculated. If b2 - 4ac is negative or 0, the code can jump to routines that handle these two special cases.
.DATA
a REAL4 3.0
b REAL4 7.0
cc REAL4 2.0
posx REAL4 0.0
negx REAL4 0.0
.CODE
.
.
.
; Solve quadratic equation - no error checking
; The formula is: -b +/- squareroot(b2 - 4ac) / (2a)
fld1 ; Get constants 2 and 4
fadd st,st ; 2 at bottom
fld st ; Copy it
fmul a ; = 2a
fmul st(1),st ; = 4a
fxch ; Exchange
fmul cc ; = 4ac
fld b ; Load b
fmul st,st ; = b2
fsubr ; = b2 - 4ac
; Negative value here produces error
fsqrt ; = square root(b2 - 4ac)
fld b ; Load b
fchs ; Make it negative
fxch ; Exchange
fld st ; Copy square root
fadd st,st(2) ; Plus version = -b + root(b2 - 4ac)
fxch ; Exchange
fsubp st(2),st ; Minus version = -b - root(b2 - 4ac)
fdiv st,st(2) ; Divide plus version
fstp posx ; Store it
fdivr ; Divide minus version
fstp negx ; Store it
The examples in online help contain an enhanced version of this procedure.
The math coprocessors have several instructions that set control flags in the status word. The 8087-family control flags can be used with conditional jumps to direct program flow in the same way that 8086-family flags are used. Since the coprocessor does not have jump instructions, you must transfer the status word to memory so that the flags can be used by 8086-family instructions.
An easy way to use the status word with conditional jumps is to move its upper byte into the lower byte of the processor flags, as shown in this example:
fstsw mem16 ; Store status word in memory
fwait ; Make sure coprocessor is done
mov ax, mem16 ; Move to AX
sahf ; Store upper word in flags
The SAHF (Store AH into Flags) instruction in the example above transfers AH into the low bits of the flags register.
You can save several steps by loading the status word directly to AX on the 80287 with the FSTSW and FNSTSW instructions. This is the only case in which data can be transferred directly between processor and coprocessor registers, as shown in this example:
fstsw ax
The coprocessor control flags and their relationship to the status word are described in Section 6.2.4.4, “Control Registers.”
The 8087-family coprocessors provide several instructions for comparing operands and testing control flags. All these instructions compare the stack top (ST) to a source operand, which may either be specified or implied as ST(1).
The compare instructions affect the C3, C2, and C0 control flags, but not the C1 flag. Table 6.3 shows the flags set for each possible result of a comparison or test.
Table 6.3 Control-Flag Settings after Comparison or Test
After FCOM | After FTEST | C3 | C2 | C0 |
ST > source | ST is positive | 0 | 0 | 0 | |
ST < source | ST is negative | 0 | 0 | 1 | |
ST = source | ST is 0 | 1 | 0 | 0 | |
Not comparable | ST is NAN or projective infinity , 1 | 1 | 1 |
Variations on the compare instructions allow you to pop the stack once or twice and to compare integers and zero. For each instruction, the stack top is always the implied destination operand. If you do not give an operand, ST(1) is the implied source. With some compare instructions, you can specify the source as a memory or register operand.
All instructions summarized in the following list have implied operands: either ST as a single-destination operand or ST as the destination and ST(1) as the source. These are the instructions for comparing and testing flags.
Some instructions have a wait version and a no-wait version. The no-wait versions have N as the second letter.
Instruction | Description |
FCOM | Compares the stack top to the source. The source and destination are unaffected by the comparison. | |
FTST | Compares ST to 0. | |
FCOMP | Compares the stack top to the source and then pops the stack. | |
FUCOM, FUCOMP, FUCOMPP | Compare the source to ST and set the condition codes of the status word according to the result (80386/486 only). | |
F[[N]]STSW mem2byte, Stores the status word in memory. | ||
FXAM | Sets the value of the control flags based on the type of the number in ST. | |
FPREM | Finds a correct remainder for large operands. It uses the C2 flag to indicate whether the remainder returned is partial (C2 is set) or complete (C2 is clear). (If the bit is set, the operation should be repeated. It also returns the least-significant three bits of the quotient in C0, C3, and C1.) | |
FNOP | Copies the stack top onto itself, thus padding the executable file and taking up processing time without having any effect on registers or memory. | |
FDISI, FNDISI, FENI, FNENI | Enables or disables interrupts (8087 only). | |
FSETPM | Sets protected mode. Requires a .286P or .386P directive (80287, 80387, and 80486 only). |
The following example illustrates some of these instructions. Notice how conditional blocks are used to enhance 80287 code.
.DATA
down REAL4 10.35 ; Sides of a rectangle
across REAL4 13.07
diamtr REAL4 12.93 ; Diameter of a circle
status WORD ?
P287 EQU (@Cpu AND 00111y)
.CODE
.
.
.
; Get area of rectangle
fld across ; Load one side
fmul down ; Multiply by the other
; Get area of circle: Area = PI * (D/2)2
fld1 ; Load one and
fadd st, st ; double it to get constant 2
fdivr diamtr ; Divide diameter to get radius
fmul st, st ; Square radius
fldpi ; Load pi
fmul ; Multiply it
; Compare area of circle and rectangle
fcompp ; Compare and throw both away
IF p287
fstsw ax ; (For 287+, skip memory)
ELSE
fnstsw status ; Load from coprocessor to memory
mov ax, status ; Transfer memory to register
ENDIF
sahf ; Transfer AH to flags register
jp nocomp ; If parity set, can't compare
jz same ; If zero set, they're the same
jc rectangle ; If carry set, rectangle is bigger
jmp circle ; else circle is bigger
nocomp: ; Error handler
.
.
.
same: ; Both equal
.
.
.
rectangle: ; Rectangle bigger
.
.
.
circle: ; Circle bigger
Additional instructions for the 80387/486 are FLDENVD and FLDENVW for loading the environment; FNSTENVD, FNSTENVW, FSTENVD, and FSTENVW for storing the environment state; FNSAVED, FNSAVEW, FSAVED, and FSAVEW for saving the coprocessor state; and FRSTORD and FRSTORW for restoring the coprocessor state.
The size of the code segment, not the operand size, determines the number of bytes loaded or stored with these instructions. The instructions ending with W store the 16-bit form of the control register data, and the instructions ending with D store the 32-bit form. For example, in 16-bit mode FSAVEW saves the 16-bit control register data. If you need to store the 32-bit form of the control register data, use FSAVED.
Some of the flags of the seven 16-bit control registers control coprocessor operations, while others maintain the current status of the coprocessor. In this sense, they are much like the 8086-family flags registers (see Figure 6.8).
Of the control registers, only the status word register is commonly used (the others are used mostly by systems programmers). The format of the status word register is shown in Figure 6.9, which shows how the coprocessor control flags align with the processor flags. C3 overwrites the zero flag, C2 overwrites the parity flag, and C0 overwrites the carry flag. C1 overwrites an undefined bit, so it cannot be used directly with conditional jumps, although you can use the TEST instruction to check C1 in memory or in a register. The status word register also overwrites the sign and auxiliary-carry flags, so you cannot count on their being unchanged after the operation.