Reverse-engineering the classic Intel 8087’s tangent algorithm: greater than CORDIC


I hope you are not bored with the 8087, as a result of I’ve one other article about
Intel’s floating-point chip.1
In 1980, Intel launched the 8087, making floating-point operations a lot quicker
within the IBM PC and different programs.
In this text, I take a look at the algorithm behind the chip’s tangent instruction.
One common method for trigonometric features is an algorithm known as CORDIC.
Another method is a polynomial approximation. The 8087 mixed the 2
to acquire each excessive accuracy and excessive efficiency.
The 8087 offered an unlimited speedup over the 8086 microprocessor, computing a tangent in
90 microseconds fairly than 13,000 microseconds.2

By inspecting the circuitry and microcode of the 8087,
I can clarify the algorithm behind the tangent instruction, known as FPTAN.
To discover the 8087’s circuitry, I popped the lid off a chip with a chisel
and created a high-resolution picture with a microscope.
The microcode ROM is the massive rectangular area within the heart of the die, holding the 1648 micro-instructions that management the chip.
The backside half of the chip (crimson field) is the datapath, the circuitry that performs floating-point calculations on 80-bit values.3

An in depth-up of the 8087’s datapath, exhibiting practical blocks which are utilized by FPTAN. Click this picture (or every other) for a bigger model.

Zooming in on the datapath reveals the related practical items.
The exponent ROM holds fastened exponent values that the algorithms want.
The fixed ROM holds constants, together with the constants utilized by the CORDIC algorithm.
The shifter is a big element; it shifts a 64-bit worth left or proper by arbitrary quantities.
The adder is the center of the 8087’s calculations; in addition to offering addition and subtraction, it’s utilized in a loop for multiplication, division, and sq. roots.
The B register holds one enter to the adder, whereas a number of sources can present the opposite enter. The sum register holds the adder’s output.
The eight stack registers and the non permanent registers maintain floating-point numbers.
Finally, the shift register holds 16 standing bits for the CORDIC calculations.

The CORDIC algorithm

CORDIC is a intelligent algorithm for rapidly computing transcendental features
with easy {hardware}: it makes use of shift and add directions together with desk lookups,
however does not want multiplication or division.
This algorithm dates again to 1956, when it was developed for the B-58 Hustler,
the primary bomber able to flying at Mach 2.
The plane had an analog navigation pc, however analog elements offered restricted accuracy.
Engineer Jack Volder was given the duty of designing a digital pc to exchange
the analog pc.7
One key drawback was that an analog pc can simply generate sines and cosines
with an electromechanical machine known as a resolver.
But trigonometric features are tough to provide digitally, particularly with the
sluggish transistors of that period.

A Convair B-58A Hustler, on display in San Antonio, TX (details).

A Convair B-58A Hustler, on show in San Antonio, TX (details).

Jack Volder got here up with a quick solution to calculate trigonometric features
with easy {hardware}.
He known as the algorithm—and the pc that applied it—CORDIC: “COordinate Rotation DIgital Computer”.
CORDIC converts an angle to a vector, the place
the vector’s coordinates present the required trig features.
The trick is to interrupt down the angle right into a sequence of particular angles, angles that
make vector rotation straightforward.
These particular angles are precomputed and saved in a desk, so the CORDIC calculation
will be carried out rapidly, even on Nineteen Fifties {hardware}.
Each CORDIC iteration supplies a further little bit of accuracy, so the algorithm converges
quickly. CORDIC grew to become common, together with in scientific calculators, which used decimal CORDIC as an alternative of binary.

Some trigonometry

I’ll attempt to maintain the maths to a minimal, however on this part I’ll give a fast clarification
of how CORDIC works.
The diagram beneath critiques how trig features are associated to the coordinates of a degree.
Suppose you may have an angle θ; it specifies a degree (X, Y) on the unit circle.
The fundamental formulation are X=cos θ, Y=sin θ, and Y/X = tan θ.
Thus, when you can decide the coordinate (X, Y), then you’ll be able to decide the worth of the
trig features.
If the purpose is not on the unit circle, e.g. (X’, Y’), then you’ll be able to nonetheless simply decide
tan θ. (Spoiler: that is what the 8087 does.) However, sin θ and cos θ grow to be messy.4

The relationship between an angle, the X and Y coordinates, and the trig functions.

The relationship between an angle, the X and Y coordinates, and the trig features.

If you’ve got carried out any pc graphics, you’ve got in all probability seen how a rotation matrix can rotate a degree by an angle. (If you are not aware of rotation matrices, you’ll be able to examine them here or simply belief that it really works.)
Multiplying a degree (X, Y) by the rotation matrix yields the brand new level (X’, Y’) as proven beneath.

A point can be rotated by using a rotation matrix.

A degree will be rotated by utilizing a rotation matrix.

Unfortunately, for the reason that rotation matrix (1, beneath) requires sin and cos, it does not seem to be it helps remedy our drawback.
However, we are able to divide the matrix by cos θ; this appears even much less useful since now the matrix (2) wants tan, which is what we need to consider. (Moreover, the vector’s size will develop.)
But the important thing to CORDIC is to make use of particular angles, αn = arctan(2-n).
When we substitute considered one of these particular angles into the matrix, we get matrix (3), which is simple to guage in {hardware}: multiplying by an influence of two will be carried out by shifting the bits.

Simplifying the rotation matrix.

Simplifying the rotation matrix.

Applying matrix (3) to the purpose (X,Y) provides the equations (4).
These are key equations for the CORDIC course of.
The vital factor is that these equations are quick and simple to compute in machine language or
{hardware}, as the one operations are addition, subtraction, and binary shifting.

With that background, we are able to see how CORDIC works.
First, we break down the specified enter angle into a mixture of particular angles that
provides as much as the specified angle.5
Then we apply the rotation system above for every particular angle,
beginning with the unit vector (1, 0). The result’s a degree
(X, Y) on the desired angle, after which the specified tangent is just Y/X.6
Since the desk of particular angles is precomputed, the arctan operations do not decelerate the method.
As an apart, after the primary few phrases, the particular angles method 2-n,
in order that they shrink by roughly an element of two at every step.

To summarize, the CORDIC algorithm consists of looping by means of a desk of saved angles.
If the saved angle is lower than the specified angle, the saved angle is subtracted from the
desired angle to yield a brand new desired angle and the equations above (shifts, add, and subtract) are utilized to yield a brand new vector.
At the top, the tangent of the unique angle is given by Y/X.

The rational polynomial approximation

The accuracy of CORDIC relies on the variety of phrases which are used. With 16 phrases, the
accuracy is roughly 2-16, or 16 bits of accuracy.
To get 64 bits of accuracy would require calculating 64 phrases (and a desk of 64 particular angles).
To get a solution quicker, the 8087 makes use of 16 bits of CORDIC and makes use of one other algorithm for
the remaining angle.
(The remaining angle is the hole between the sum of particular CORDIC angles and the specified angle,
so it is rather small, round 2-16.)

For the remaining angle, the 8087 makes use of a Padé approximant, which is the ratio of two polynomials.
There’s an entire household of Padé approximations, relying on the order of the polynomials.
The 8087 makes use of a easy system: 3x/(3-x2).8
Although this approximation is easy, it is rather correct for small values; its error is proportional to x4.
Since x<2-16, the error can be lower than 2-64, assembly the 64-bit accuracy
requirement for the 8087.
Moreover, the 8087 does not must carry out the division within the rational polynomial since
FPTAN returns a separate numerator and denominator. Thus, the division is “free”.

The tangent function (red), rational approximation (blue), and Taylor series (green).
Disclaimer: The Taylor series isn't as bad as it appears, since the relevant range is very close to 0.
Graph generated with Desmos.

The tangent operate (crimson), rational approximation (blue), and Taylor sequence (inexperienced).
Disclaimer: The Taylor sequence is not as dangerous because it seems, for the reason that related vary could be very near 0.
Graph generated with Desmos.

If you’ve got studied calculus, you would possibly suppose {that a} Taylor sequence polynomial is the best way to go,
however the ratio of two polynomials is healthier.
(One cause is that tangent blows as much as infinity at π/2.
A polynomial will not blow up, however the ratio of polynomials can, so it suits the tangent operate higher.)
The graph above compares the tangent operate (crimson), the rational approximation (blue),
and the third-order Taylor sequence (inexperienced).

Putting the items collectively: the 8087 algorithm

The 8087’s tangent algorithm has three elements: figuring out the CORDIC resolution bits (known as pseudo-division),
computing the rational approximation, and making use of the rotation equations based mostly on the
CORDIC resolution bits (known as pseudo-multiplication).9

In extra element, step one determines which particular angles so as to add to approximate the
enter angle.
Each particular angle is in comparison with the remaining enter angle and subtracted whether it is smaller.
If the angle is subtracted, a 1 is recorded; in any other case, a 0 is recorded.
Since this course of is much like how lengthy division subtracts (or does not subtract) successive
shifted variations of the divisor, producing 1s or 0s for the quotient, the method is named pseudo-division.
Note that the rotations aren’t utilized on this step. Instead, this step decides which rotations
to use later.

The diagram beneath reveals this course of utilized to the enter angle 0.95 radians.10
The course of generates the sequence of bits
[1,0,0,1,0,1,0,1,0,0,1,0,0,1,1,1],
the place the leftmost bit signifies arctan(20) and so forth.
The angle is lowered by roughly an element of two at every step, so the residual angle is
very small.

In the first phase of the CORDIC algorithm—pseudo-division—the input angle is reduced by special angles, leaving a residual angle at the end. ("rad" is radians, not the unit of radiation.)

In the primary section of the CORDIC algorithm—pseudo-division—the enter angle is lowered by particular angles, leaving a residual angle on the finish. (“rad” is radians, not the unit of radiation.)

Next, the tangent of the remaining angle is calculated with the rational approximation
operate, 3x/(3-x2).
The result’s used because the preliminary vector for the following step.
The division is just not carried out right here; as an alternative, the numerator turns into Y within the preliminary vector
and the denominator turns into X. Thus, the costly division step is prevented, because it
occurs implicitly within the reply.
Multiplying by 3 is simple (shift left and add), so the one costly operation at this
step is squaring the angle, which requires a full 64-bit multiplication.

The closing step applies every CORDIC rotation if the corresponding resolution bit from step one is
1.
Since that is considerably analogous to binary multiplication, which provides the multiplicand
at every step if the multiplier bit is 1, this step is named pseudo-multiplication.
As described earlier, every rotation is computed with shifts, addition, and subtraction,
so every rotation is cheap.
Each rotation on this step corresponds to an angle discount in step one.
The rotations are utilized in
reverse order—the smallest one first—to scale back rounding error.
To accomplish this, the choice bits are saved in a 16-bit shift register within the
first step, and shifted out in reverse order on this step.

In the last phase of the CORDIC algorithm—pseudo-multiplication—a vector is rotated multiple times by applying shifts and adds. The final vector provides the tangent.

In the final section of the CORDIC algorithm—pseudo-multiplication—a vector is rotated a number of occasions by making use of shifts and provides. The closing vector supplies the tangent.

The diagram above reveals the method utilized to the enter 0.95.
The preliminary vector (inexperienced) comes from the rational approximation operate; it is rather near
(3, 0), however barely rotated because of the tiny residual angle.
Each rotation step generates a brand new vector that matches the angle from the corresponding
step within the first half; I present two of the rotation matrices.
Note that the vectors diverge from the circle—a consequence of dividing every matrix by cos θ—however this does not
have an effect on the tangent.

The FPTAN instruction is considerably peculiar because it does not return the tangent instantly.
Instead, it returns the Partial Tangent: the 2 coordinate values
(X and Y) that may be divided to provide the tangent.
One would possibly marvel why the chip did not do the division routinely.
The motivation was that division
was sluggish again then, and omitting the division allowed for optimizations in some instances.

Relevant {hardware} particulars of the 8087

In this part, I’ll clarify some options of the 8087 which are vital for the microcode implementation of FPTAN.

The 8087 helps quite a lot of knowledge varieties,
however internally, every part is saved as an 80-bit floating-point quantity known as a “non permanent actual”.
A quantity has three elements: an indication bit, a 15-bit exponent, and a 64-bit significand (the fractional half),
In most instances, a floating-point quantity is represented by signal × significand × 2exponent.
The benefit of floating-point numbers is that the exponent permits them to cowl an enormous vary, from very small to very massive.
The significand is a 64-bit binary variety of the shape 1.bbb…:
a number one 1, adopted by the binary level (the binary equal of the decimal level) and the remainder of the bits.11
One vital element is that the exponent is saved with a “bias” of 16383 added to it. Thus, the saved exponent is all the time optimistic, even when the actual
exponent is unfavorable. For occasion, an exponent of 1 is saved as 0x4000, and an exponent of -16 is saved as 0x3fef.

The 80-bit temporary real format and the register formats. The dot indicates the binary point, analogous to the decimal point.

The 80-bit non permanent actual format and the register codecs. The dot signifies the binary level, analogous to the decimal level.

To use the 8087, a programmer shops values in its eight inner registers, organized as a stack. Each register holds an 80-bit floating-point quantity.
To optimize efficiency,
every worth within the register stack additionally has an related “tag” worth:
zero, legitimate, particular, or empty.
A worth of zero is tagged as zero.
A “regular” floating-point worth is tagged as legitimate. If the worth is infinity, Not a Number (NaN), or a denormalized worth,
then it’s tagged as particular.
Finally, an empty tag signifies {that a} register doesn’t maintain a price; this permits detection of
stack underflow (studying an empty register) or stack overflow (storing to a non-empty
register).

The 8087 additionally has non permanent registers that it makes use of internally: tmpA, tmpB, and tmpC.
(These registers are closely used for the tangent calculation.)
tmpA and tmpB are 80-bit registers, alongside
with two tag bits.
However, tmpC is completely different: it does not have an indication, exponent, or tag.
Moreover,
the significand is 68 bits; tmpC has a further
bit to forestall overflow and three extra low-order bits for rounding, referred to as
guard, spherical, and sticky.

In the microcode,
every 16-bit micro-instruction performs one low-level operation.
Many of the operations transfer knowledge from one register to a different.
Other operations present conditional jumps, subroutine calls, and returns.
A bit shift operation takes two micro-instructions: the primary strikes a price to the shifter and
configures the path and quantity of shift. The second micro-instruction (which can be
a distance after the primary) strikes the end result from the shifter to a vacation spot.

Similarly, addition takes two micro-instructions. The first micro-instruction
specifies the primary argument so as to add and selects varied choices reminiscent of
carry and rounding.
(For subtraction, an possibility enhances the second argument.)
The second argument all the time comes from the B register, which confusingly is
unrelated to the tmpB register.
The second micro-instruction strikes the end result from the sum register to a vacation spot.

Implementation of the tangent algorithm in microcode

In this part, I’ll focus on some highlights of the tangent microcode.
The itemizing beneath reveals the 8087’s microcode, together with my feedback.
Each line signifies a 16-bit micro-instruction.
The management circulation is a bit sophisticated, so I’ve put a flowchart within the footnotes.12

FPTAN:
#1039 st(0) -> tmpA        retailer argument in tmpA
#1040 stackPtr--           test if room on stack to push end result
#1041 stack overflow?
#1042 jmp #1022 if tmp empty/particular/overflow/div exit if dangerous argument or stack overflow
#1043 jmp #1045 if tmpA:tag ZERO is argument 0?
#1044 besides:precision     precision exception apart from 0
#1045 expconst 0x3ff0      Test if exponent >= -16
#1046 adder: tmpA:exp + 0 cin=1 argument exponent + 1
#1047 expConst -> Breg
#1048 adder: sumreg:frac - Breg cin=1 subtract -15
#1049 jmp #1061 if adder pos CORDIC if exponent >= -16
#1050 expconst 0x3fff      non-CORDIC path:
#1051 tmpA:exp -> Breg     test unique exponent
#1052 expConst -> tmpB:frac in opposition to exponent 0
#1053 adder: tmpB:frac - Breg cin=1 subtract
#1054 sumreg:frac -> expConv 3fff - exp (i.e. -exp unbiased)
#1055 sumreg:frac -> shiftcount
#1056 jmp #1082 if exponent[6:14] != 0 if exp > -64 goto rational approximation
#1057 excessive bit -> tmpB:frac in any other case return unique argument
#1058 expConst -> tmpB:signal,exp push 1 for X (denom): result's orig angle
#1059 tmpB -> st(0)
#1060 RNI
#1061 expconst 0x0010      CORDIC path
#1062 sumreg:frac -> loopcounter loopcounter = exp + 16
#1063 tmpA:frac -> tmpC    tmpC = unique angle
#1064 trig const -> Breg/rnd
#1065 adder: tmpC - Breg cin=1, roundmode subtract first CORDIC angle
#1066 adder signal -> cordic, shl save bit  in shift register
#1067 jmp #1079 if not adder pos
#1068 sumreg:frac,rnd -> tmpC
#1069 adder: tmpA:exp + 0 cin=1 increment exp if first angle used
#1070 sumreg:frac -> tmpA:exp
#1071 jmp #1079
#1072 shift tmpC L 0 bytes, 1 bits high of CORDIC scan loop
#1073 shift L -> tmpC      shift angle left
#1074 trig const -> Breg/rnd
#1075 adder: tmpC - Breg cin=1, roundmode subtract CORDIC angle
#1076 adder signal -> cordic, shl save resolution bit in shift register
#1077 jmp #1079 if not adder pos
#1078 sumreg:frac,rnd -> tmpC
#1079 jmp #1072 if not const latch zero backside of CORDIC scan loop
#1080 tmpC -> tmpA:frac    replace tmpA, tmpB
#1081 expConst -> shiftcount 16 -> shiftcount (in any other case based mostly on exponent)
#1082 tmpA:frac -> tmpB:frac Padé approximation
#1083 tmpA:frac -> Breg    tmpA = ang
#1084 name SQUARE          sq. the angle
#1085 shift sumreg:frac R rely byte bit
#1086 shift R -> sumreg:frac shift twice to scale sq.
#1087 shift sumreg:frac R rely byte bit
#1088 shift R -> Breg      Breg = ang^2
#1089 1.1 -> tmpB:frac     3 (with exp 1)
#1090 adder: tmpB:frac - Breg cin=0
#1091 sumreg:frac -> tmpB:frac tmpB (X) = 3-ang^2
#1092 shift tmpA:frac R 0 bytes, 1 bits
#1093 shift R -> Breg/rnd
#1094 adder: tmpA:frac + Breg cin=0, roundmode ang + ang/2
#1095 sumreg:signal,frac,rnd -> tmpC tmpC (Y) = 3×ang with applicable exponent
#1096 expconst 0x3ff0      exponent -15
#1097 expConst -> Breg
#1098 adder: tmpA:exp - Breg cin=1
#1099 jmp #1118 if not adder pos skip if exponent too small
#1100 shift tmpC R 0 bytes, 1 bits CORDIC pseudo-multiplication path
#1101 #15 -> loopcounter
#1102 shift R -> tmpC
#1103 jmp #1113 if not cordic[0] CORDIC: if saved resolution bit...
#1104 shift tmpC R loopcount ship tmpC (Y) to shifter
#1105 tmpC -> Breg/rnd     ship tmpC to adder
#1106 adder: tmpB:frac + Breg cin=0, roundmode tmpB (X) not shifted as a result of tmpC scaled
#1107 sumreg:signal,frac,rnd -> tmpC tmpC = tmpC + tmpB (implicit >>n)
#1108 shift R -> sumreg:frac,rnd shifted outdated tmpC to sumreg
#1109 shift sumreg:frac,rnd R loopcount shift proper once more as a result of tmpC scaled
#1110 shift R -> Breg/rnd  and ship to B reg
#1111 adder: tmpB:frac - Breg cin=1, roundmode
#1112 sumreg:frac,rnd -> tmpB:frac tmpB = tmpB - tmpC>>n
#1113 cordic shr           shift out shift register bit
#1114 shift tmpC R 0 bytes, 1 bits begin shift of tmpC (Y)
#1115 jmp #1118 if cordic==0 carried out if shift register empty
#1116 shift R -> tmpC      shift tmpC (Y) proper
#1117 jmp #1103 if not const latch zero carried out if counter at zero
#1118 expconst 0x3fff      CORDIC carried out
#1119 tmpB:frac -> sumreg:frac Test high little bit of tmpB (X)
#1120 jmp #1124 if reg bit 63 If set, use exponent 0
#1121 expconst 0x3ffe      Otherwise, use exponent -1 and
#1122 shift sumreg:frac L 0 bytes, 1 bits shift left one bit to normalize
#1123 shift L -> tmpB:frac
#1124 expConst -> tmpB:signal,exp retailer exponent in tmpB
#1125 tmpC -> tmpA:frac    retailer tmpC (Y) in tmpA
#1126 tmpC -> sumreg:frac,signal
#1127 jmp #1132 if not sumreg[64] take a look at overflow bit
#1128 shift tmpC R 0 bytes, 1 bits if set, normalize by shifting proper
#1129 shift R -> tmpA:frac
#1130 adder: tmpA:exp + 0 cin=1 and increment exponent by 1
#1131 sumreg:frac -> tmpA:exp
#1132 tmpB -> st(0)        tmpB to high of stack: X (denom)
#1133 stackPtr++
#1134 tmpA -> st(0)        tmpA to st(1): Y (numerator)
#1135 stackPtr--
#1136 RNI                  End of FPTAN

The FPTAN microcode begins at handle #1039 (decimal).
The microcode begins by shifting the argument from the highest of the stack to the tmpA register.
If the highest worth is empty, this means a stack underflow. If the following aspect on
the stack (the place that may maintain the end result) is just not empty, this means a stack
overflow. In both case, the microcode signifies an “invalid” exception and ends the instruction.

One peculiar characteristic of the 8087 is that it flags a end result with a “precision” exception if
the end result is just not precise. (This occurs very steadily, even for, say, 1/10.)
The solely tangent that the 8087 can compute exactly is tan(0), so all different inputs
lead to a precision exception (#1044).

Next, the argument’s exponent is examined, splitting the execution into three paths.
If the exponent is -64 or much less, the argument is so small that
the tangent equals the argument, throughout the accuracy of the system.13
In this case (#1057), the unique argument is returned unchanged (with the denominator 1 pushed), and the code ends.
The second case is that if the argument’s exponent is -17 or much less. In this case,
the CORDIC step is skipped, and the routine jumps on to the rational approximation (#1082).

CORDIC pseudo-division

The most fascinating case is the CORDIC path (#1061), taken if the exponent is between -1 and -16.
Conceptually, the code performs 16 CORDIC steps, utilizing 16 saved angles.
However, the code is optimized, skipping the massive angles for smaller
inputs.
Specifically, the loop counter is initialized based mostly on the
exponent of the enter angle (#1062).
The CORDIC pseudo-division loop (#1061–#1080) exams the angles, subtracting ones that are not too large.14

The resolution outcomes are recorded in a 16-bit shift register, positioned on the far proper facet of the die.
Presumably, that is the place the structure had some unused house.
Since the shift register is loaded and unloaded serially, it does not want entry to the
inner fraction bus, simply two traces to shift the bits out and in, so the shift
register might be positioned in in any other case unused house.

The code is optimized to make use of 64-bit integer arithmetic fairly than floating-point arithmetic.
This makes it difficult to grasp the code since values
should be considered extra as fixed-point numbers with
implicit exponents.
These exponents aren’t saved anyplace, however will be decided by analyzing the algorithm.
Even the angle constants within the ROM wouldn’t have express exponents.15
Moreover, the implicit exponents change for every step by means of the loop to protect accuracy.
Roughly talking, every cycle of the loop reduces the values by an element of two, and the
angle constants shrink accordingly.
If the values had a set exponent, they might find yourself with 16 main zeros, losing
precision. But by scaling the values every cycle (with a left shift),
the numbers proceed to make use of the total 64 bits.
In different phrases, the {hardware} is performing quick integer arithmetic, however mathematically
you’ll be able to consider it as fixed-point with exponents that do not bodily exist within the chip.
See the desk within the footnotes16 for particulars on how the implicit exponents change by means of
the loop.

Rational polynomial approximation

After the CORDIC pseudo-division loop, the microcode calculates the rational polynomial
approximation (#1082).
The small-angle path rejoins the execution circulation right here.
The microcode to calculate the polynomial approximation has a couple of fascinating options.
The most time-consuming half is the SQUARE routine, which multiplies a fixed-point quantity by itself.
Multiplication is sophisticated, so I’ll give the main points in a later publish.
But in short, the 8087 makes use of Booth’s Algorithm to
multiply by two bits at a time (radix 4), so it’s twice as quick as common binary
multiplication.
The loop to carry out the shifts and provides is applied in {hardware}, fairly than microcode.
That is, one microcode instruction performs 32 additions: the {hardware} exams the
bits, does the suitable add or subtract, updates the loop counter, and loops.
For efficiency, the shifting is finished with specialised shifters, not the 8087’s general-purpose
shifter.

Except for the sq., the polynomials are calculated with additions fairly than multiplications.
The fixed 3 does not come from the fixed ROM, however from special-purpose transistors that
set the highest two bits of the significand; that is normally used to produce an NaN.
(If the implicit exponent is 1, this corresponds to three.)
Subtracting the sq. yields the numerator. For the denominator, the angle is shifted
proper (i.e. divided by 2) and added to itself.
This performs a multiplication by 3 (technically by 1.5, however growing the implicit
exponent to 1 turns this into 3).
It could be costly to divide the numerator by the denominator; as an alternative, the numerator
and denominator are used because the preliminary Y and X values for the next CORDIC steps.

CORDIC pseudo-multiplication

Next is the CORDIC loop the place the rotations are utilized to the vector (#1100).
The rotations are utilized within the reverse order from how they had been computed in step one:
the smallest rotations are utilized first to protect accuracy.
Bits are shifted out of the shift register to point if the rotation must be utilized or
not. As quickly because the shift register is all zeros, the loop stops.
In different phrases, smaller angles undergo the loop just some occasions, not the total 16 occasions.

A close-up of the microcode ROM under the microscope. A transistor is formed where a vertical polysilicon line crosses doped silicon (pink). The 8087's ROM is unusual: it uses four transistor sizes, so it stores two bits per transistor, twice the density of a regular ROM.

An in depth-up of the microcode ROM underneath the microscope. A transistor is shaped the place a vertical polysilicon line crosses doped silicon (pink). The 8087’s ROM is uncommon: it makes use of 4 transistor sizes, so it shops two bits per transistor, twice the density of an everyday ROM.

The rotations are utilized by shifts and provides (or subtracts), however conserving observe of the
implicit exponents is a bit difficult.
Since the vector begins virtually horizontal, the X worth is roughly 3, and the Y worth
is round 2-16.
As the vector rotates, X stays roughly 3, however Y doubtlessly will increase by an element of two
every time.
To present the utmost accuracy for every quantity,
the exponent for X (tmpB) is 1, whereas the exponent for Y (tmpC) begins off at
-14 and will increase by 1 every loop as Y is shifted proper.
(Remember that these exponents aren’t saved anyplace however are implicit.)
Since the 2 registers have completely different (implicit) exponents, the values should be shifted
earlier than including.
The shift quantities aren’t intuitive; the desk16 within the
footnotes might assist make clear.

After the CORDIC loop,
the ultimate a part of the code (#1118) normalizes the X and Y values, creating the floating-point
values which are returned from the instruction.
Recall that these aren’t “actual” floating-point values at this level, in order that they
may have adjustment.
Specifically, if X does not have a
main 1, it’s shifted left one place, whereas if Y has two main digits, it
is shifted proper one place. In both case, the exponent is adjusted accordingly.
The X and Y values are placed on the stack (#1132) to finish the instruction.

Conclusions

The FPTAN instruction is pretty sluggish as 8087 directions go, attributable to its complexity.
The documentation says that it takes usually 450 clock cycles.
(The time has a wide variety; relying on the worth, it could take 30 to 540 clock cycles.)
For the worth I examined (0.95), 33% of the time is within the CORDIC
pseudo-division, 15% within the rational polynomial (principally the squaring operation), 47% within the CORDIC pseudo-multiplication, and 5% overhead.

CORDIC is a well-liked solution to compute trig features, however there are alternate options reminiscent of polynomials.
The 8087 is uncommon as a result of it combines CORDIC and a rational polynomial.
For the Pentium, Intel moved from CORDIC to polynomial approximations;
the Pentium’s fast multiplication circuitry made polynomials sensible.
Nowadays, Intel has libraries reminiscent of MKL (Math Kernel Library)
and
Short Vector Math Library (SVML) that present extremely optimized implementations tailor-made
to Intel {hardware}. These libraries are stated to make use of polynomial approximations,
utilizing parallel directions (SIMD) for efficiency, fairly than specialised {hardware}.
With these libraries, x87 operations and 80-bit floats are mostly obsolete.
Nonetheless, I hope you’ve got loved this take a look at an unique 8087 algorithm.

I plan to proceed reverse-engineering the 8087 microcode;
for updates, comply with me on
Bluesky (@righto.com),
Mastodon (@[email protected]),
or RSS.
I’ve been engaged on this with the members of the “Opcode Collective”, particularly Smartest Blob and Gloriouscow, who transformed the
ROM photographs to microcode knowledge and extensively analyzed the contents.
See the 8087 repository on GitHub for extra.
AI assertion: Despite the presence of the em sprint, no AI was used within the writing of this text (details). The Opcode Collective used an ML algorithm to categorise every ROM cell to extract the microcode.

Notes and references



Source link