home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / progm / a86d86-1.zip / A07.DOC < prev    next >
Text File  |  1990-01-25  |  21KB  |  481 lines

  1. CHAPTER 7   THE FLOATING-POINT PROCESSOR
  2.  
  3.  
  4. In this chapter, we'll refer to the various Central Processing
  5. Units (CPUs) as the "86".  Thus "86" refers to either the 8088,
  6. 8086, 80186, 80286, etc.  We'll refer to the various coprocessors
  7. as the "87".  Thus "87" refers to either the 8087, the 287, the
  8. 387, or the special IIT-2C87 processor.
  9.  
  10.  
  11. The 8087 and 287 Coprocessors
  12.  
  13. All IBM-PC's, and most clones, contain a socket for a floating
  14. point coprocessor.  If you shell out between $80 and $300, and
  15. plug the appropriate chip into that socket, then a host of
  16. floating point instructions is added to the assembly language
  17. instruction set.
  18.  
  19. The original IBM-PC, and the XT, accept the original floating
  20. point chip, the 8087.  The AT accepts a later update, the 287.
  21. From a programming standpoint, the two chips are nearly
  22. identical: the 287 adds the instructions FSETPM and FSTSW AX, and
  23. ignores the instructions FENI and FDISI.  There is, however, a
  24. rather nasty design flaw in the 8087, that was corrected in the
  25. 287.
  26.  
  27. To understand the flaw, you must understand how the 86 and 87
  28. work as coprocessors. Whenever the 86 sees a floating point
  29. instruction, it communicates the instruction, and any associated
  30. memory operands, to the 87.  Then the 86 goes on to its next
  31. instruction, operating in parallel with the 87.  That's OK, so
  32. long as the following instructions don't do one of the following:
  33.  
  34.   1. Execute another floating point instruction; or
  35.  
  36.   2. Try to read the results of the still-executing floating
  37.      point instruction.
  38.  
  39. If they do, then you must provide an instruction called WAIT (or
  40. synonymously FWAIT), which halts the 86 until the 87 is finished.
  41. For almost all floating point instructions, it should not be
  42. necessary to provide an explicit FWAIT; the 86 ought to know that
  43. it should wait.  For the 8087, it IS necessary to give an
  44. explicit FWAIT before each floating point instruction: that is
  45. the flaw.
  46.  
  47. Because of the flaw, all assemblers supporting the 8087 will
  48. silently insert an FWAIT code (hex 9B) before all 87
  49. instructions, except those few (the FN instructions other than
  50. FNOP) not requiring the FWAIT.  A86 provides the switch +F (the F
  51. must be capitalized), to signal that the 287 is the target
  52. processor.  A86 also provides the directive ".287", compatible
  53. with Microsoft's assembler, that you can insert into your
  54. programs to accomplish the same thing as +F. However, the actions
  55. taken by A86 and Microsoft when seeing .287 are completely
  56. disjoint!  To wit:
  57.                                                               7-2
  58.  
  59. * A86 ceases outputting FWAIT directives that are unnecessary for
  60.   the 287.  For reasons beyond my comprehension, Microsoft
  61.   continues to put them out.  Can someone enlighten me as to why
  62.   Microsoft is putting out those codes?
  63.  
  64. * A86 ignores the instructions FENI, FDISI, FNENI, and FNDISI
  65.   after it sees a .287 directive.  Microsoft continues to
  66.   assemble these instructions.
  67.  
  68. * Microsoft recognizes the new 287 instructions, if and only if
  69.   it sees the .287 directive.  A86 recognizes them even if .287
  70.   is not given.  In general, I don't attempt to police your
  71.   instruction usage-- if you use an instruction available on a
  72.   limited number of processors, I trust that you are programming
  73.   for one of those processors.
  74.  
  75. In summary, if your program will be running only on machines with
  76. a 287, you can give ".287" directive. Your programs will be
  77. significantly shorter than if they were assembled by Microsoft.
  78. If you want your programs to run on all machines containing a
  79. floating point chip, you should refrain from specifying .287.
  80.  
  81. WARNING: The most common mistake 87 programmers make is to try to
  82. read the results of an 87 operation in 86 memory, before the
  83. results are ready.  At least on my AT, the system often crashes
  84. when you do this!  If your program runs correctly when single
  85. stepped, but crashes when set loose, then chances are you need an
  86. extra explicit FWAIT somewhere.
  87.  
  88.  
  89. Extra Coprocessor Support
  90.  
  91. A86 now supports two additional coprocessors available for
  92. PC-compatibles: the 80387, available for 386-based machines, and
  93. the IIT-2C87, a 287-plug-compatible chip that adds a couple of
  94. unique instructions. The IIT-2C87 has two extra banks of on-chip
  95. 8-number stacks, that can be switched in with the FBANK
  96. instruction, and a matrix multiply instrction that uses all three
  97. banks as input.  (For details contact Specialty Software
  98. Development Corp., 110 Wild Basin Road, Austin TX 78746.) Both
  99. chips incorporate the correction to the 8087's FWAIT design flaw,
  100. so you can assemble with the .287 directive.  The extra
  101. instructions for these chips are marked by "387 only:" and "IIT
  102. only:" in the chart at the end of this chapter.
  103.  
  104.  
  105. Emulating the 8087 by Software
  106.  
  107. There is a software package provided with many compilers
  108. (Borland's Turbo C and most Microsoft compilers, for example)
  109. that emulates the 8087 instruction set.  The emulator is very
  110. cleverly implemented so that the programmer need not know whether
  111. a floating point chip will be available, or whether emulation
  112. will be necessary.  This is done by having the linker replace all
  113. floating point machine instructions with INT calls to certain
  114. interrupts, dedicated to emulation.  The interrupt handlers
  115. interpret the operands to the instructions, and emulate the 8087.
  116.                                                               7-3
  117.  
  118. You can tell A86 that the emulator might be used, by providing a
  119. +f switch in the invocation line, or in the A86 environment
  120. variable (make sure the f is lower case).  Since your program
  121. will be linked to the emulator, you must be producing an OBJ
  122. file, not a COM file, for emulation support to take effect.
  123. Whenever a floating point instruction is assembled, A86 will
  124. generate an external reference at the opcode for the instruction.
  125. Then, if the emulation package is linked with your program, the
  126. opcodes will be replaced by the INT calls. If a special
  127. non-emulation module is linked, the opcodes will be left alone,
  128. and the floating point instructions will be executed directly.
  129.  
  130.  
  131. The Floating Point Stack
  132.  
  133. The 87 has its own register set, of 8 floating point numbers
  134. occupying 10 bytes each, plus 14 bytes of status and control
  135. information.  Many of the 87's instructions cause the numbers to
  136. act like a stack, much like a Hewlett-Packard calculator.  For
  137. this reason, the numbers are called the floating point stack.
  138.  
  139. The standard name for the top element of the floating point stack
  140. is either ST or ST(0); the others are named ST(1) through ST(7).
  141. Thus, for example, the instruction to add stack element number 3
  142. into the top stack element is usually coded FADD ST,ST(3).
  143.  
  144. I find this notation painfully verbose.  Especially bad are the
  145. parentheses, which are hard to type, and which add visual clutter
  146. to the program.  To alleviate this problem while retaining
  147. language compatibility, I name my stack elements simply 0 through
  148. 7.  I recognize ST as a synonym for 0.  I allow expression
  149. elements to be concatenated; concatenation is the same as
  150. addition.  Thus, when A86 sees ST(3), it computes 0+3 = 3.  So
  151. you can code the old way, FADD ST,ST(3), or you can code the
  152. concise way, FADD 0,3 or simply FADD 3.
  153.  
  154.  
  155. Floating Point Initializations
  156.  
  157. In general, you use the 87 by loading numbers from 86 memory to
  158. the 87 stack (using FLD instructions), calculating on the 87
  159. stack, and storing the results back to 86 memory (using FST and
  160. FSTP instructions).  There are seven constant numbers built into
  161. the 87 instruction set: zero, one, Pi, and four logarithmic
  162. conversion constants.  These can be loaded using the FLD0, FLD1,
  163. FLDPI, FLDL2T, FLDL2E, FLDLG2, and FLDLN2 instructions.  All
  164. other constants must be declared in, then loaded from, 86 memory.
  165. Integer constant words and doublewords can be loaded via FILD.
  166. Non-integer constant doubleword, quadwords, and ten-byte numbers
  167. can be loaded via FLD.
  168.                                                               7-4
  169.  
  170. A86 allows you to declare constants loaded via FLD as floating
  171. point numbers, using scientific notation if you like.  As an
  172. exclusive feature, A86 allows you to use any of the 4 arithmetic
  173. functions +, -, *, / in expressions involving floating point
  174. numbers.  A86 will even do type conversion if one of the two
  175. operands is given as an integer; though for clarity I recommend
  176. that you always give floating point constants with their decimal
  177. point.
  178.  
  179.  
  180. Built-In Constant Names
  181.  
  182. A86 offers another exclusive feature: the built-in symbols
  183.  
  184.     PI   ratio of circumference to diameter of a circle
  185.  
  186.     L2T  log base 2 of 10
  187.  
  188.     L2E  log base 2 of the calculus constant e = 2.71828...
  189.  
  190.     LG2  log base 10 of 2
  191.  
  192.     LN2  natural log (base e) of 2
  193.  
  194. You can use these symbols in expressions, to declare useful
  195. constants.  For example, you can declare the degrees-to-radians
  196. conversion constant:
  197.  
  198.     DEG_TO_RAD  DT  PI/180.
  199.  
  200.  
  201.  
  202. Special Immediate FLD Form
  203.  
  204. Yet another exclusive A86 feature is the instruction form FLD
  205. constant.  This form is intended primarily to facilitate "fooling
  206. around" with the 87 when using D86; but it is also useful for
  207. quick-and-dirty programs.  For example, the instruction FLD 12.3
  208. generates the following sequence of code bytes (without
  209. explicitly using the local labels given):
  210.  
  211.     CS FLD T[M1]
  212.     JMP >M2
  213.   M1  DT 12.3
  214.   M2:
  215.  
  216. Obviously, this form is not terrifically efficient: you can
  217. always save the JMP by placing the constant outside of the
  218. instruction stream; and the CS override might not be needed.  But
  219. the form is very, very convenient!
  220.  
  221. NOTE that the preceding 2 sections imply that you can get
  222. careless and code, for example, FLD PI when you intended FLDPI.
  223. Though the two are functionally equivalent, the first form takes
  224. a whopping 17 bytes; and second, only 2 bytes.  Be careful!
  225.                                                               7-5
  226.  
  227. Floating Point Operand Types
  228.  
  229. The list of floating point instructions contains a variety of
  230. operand types.  Here is a brief explanation of those types:
  231.  
  232. 0        stands for the top element of the floating point stack.
  233.          A synonym for 0 is ST or ST(0).
  234.  
  235. i        stands for element number i of the floating point stack.
  236.          i can range from 0 through 7.  A synonym for i is ST(i).
  237.  
  238. mem10r   is a 10-byte memory quantity (typically declared with a
  239.          DT directive) containing a full precision floating point
  240.          number. Intel recommends that you NOT store your numbers
  241.          in full precision; that you use the following double
  242.          precision format instead.  Full precision numbers are
  243.          intended for storage of intermediate results (on the
  244.          stack); they exist to insure maximum accuracy for
  245.          calculations on double precision numbers, which is the
  246.          official external format of 87 numbers.
  247.  
  248. mem8r    is an 8-byte memory quantity (typically declared with a
  249.          DQ directive) containing a double precision floating
  250.          point number.  This is the best format for floating
  251.          point numbers on the 87.  The 87 takes the same amount
  252.          of time on double precision calculations as it does on
  253.          single precision.  The only extra time is the memory
  254.          access of 4 more bytes; negligible in comparison to the
  255.          calculation time.
  256.  
  257. mem4r    is a 4-byte quantity (typically defined with a DD
  258.          directive) containing a single precision floating point
  259.          number.
  260.  
  261. mem10d   is a 10-byte quantity (also defined via DT) containing a
  262.          special Binary Coded Decimal format recognized by the
  263.          FBLD and FBSTP instructions.  This format is useful for
  264.          input and output of floating point numbers.
  265.  
  266. mem4i    is a 4-byte quantity representing a signed integer in
  267.          two's-complement notation.
  268.  
  269. mem2i    is a 2-byte quantity representing a signed integer in
  270.          two's-complement notation.
  271.  
  272. mem14    and mem94 are 14- and 94-byte buffers containing the 87
  273.          machine state.
  274.                                                               7-6
  275.  
  276. Operand Choices in A86
  277.  
  278. In the "standard" assembly language, the choice of operands for
  279. floating point instructions seems inconsistent to me.  For
  280. example, to subtract stack i from 0, you must provide two
  281. operands; to do the equivalent comparison, you must provide only
  282. one operand.  A86 smooths out these inconsistencies by allowing
  283. more choices for operands: FADD i is equivalent to FADD 0,i. FCOM
  284. 0,i is equivalent to FCOM i.  The same holds for the other main
  285. arithmetic instructions.  FXCH 0,i and FXCH i,0 are allowed. So
  286. if you wish to retain compatibility with other assemblers, you
  287. should use their more restrictive instruction list, not the
  288. following one.
  289.  
  290.  
  291. The 87 Instruction Set
  292.  
  293. Following is the 87 instruction set.  The "w" in the opcode field
  294. is the FWAIT opcode, hex 9B, which is suppressed if .287 is
  295. selected.  Again, "0", "1", and "i" stand for the associated
  296. floating point stack registers, not constant numbers!  Constant
  297. numbers in the descriptions are given with decimal points: 0.0,
  298. 1.0, 2.0, 10.0.
  299.  
  300.  
  301.  
  302.     Opcode    Instruction     Description
  303.  
  304.  w  D9 F0     F2XM1           0 := (2.0 ** 0) - 1.0
  305.  w  DB F1     F4X4            IIT only: 4 by 4 matrix multiply
  306.  w  D9 E1     FABS            0 := |0|
  307.  w  DE C1     FADD            1 := 1 + 0, pop
  308.  w  D8 C0+i   FADD i          0 := i + 0
  309.  w  DC C0+i   FADD i,0        i := i + 0
  310.  w  D8 C0+i   FADD 0,i        0 := i + 0
  311.  w  D8 /0     FADD mem4r      0 := 0 + mem4r
  312.  w  DC /0     FADD mem8r      0 := 0 + mem8r
  313.  w  DE C0+i   FADDP i,0       i := i + 0, pop
  314.  w  DB E8     FBANK 0         IIT only: set bank pointer to default
  315.  w  DB EB     FBANK 1         IIT only: set bank pointer to bank 1
  316.  w  DB EA     FBANK 2         IIT only: set bank pointer to bank 2
  317.  w  DF /4     FBLD mem10d     push, 0 := mem10d
  318.  w  DF /6     FBSTP mem10d    mem10d := 0, pop
  319.                                                               7-7
  320.  
  321.  w  D9 E0     FCHS            0 := -0
  322. 9B  DB E2     FCLEX           clear exceptions
  323.  w  D8 D1     FCOM            compare 0 - 1
  324.  w  D8 D0+i   FCOM 0,i        compare 0 - i
  325.  w  D8 D0+i   FCOM i          compare 0 - i
  326.  w  D8 /2     FCOM mem4r      compare 0 - mem4r
  327.  w  DC /2     FCOM mem8r      compare 0 - mem8r
  328.  w  D8 D9     FCOMP           compare 0 - 1, pop
  329.  w  D8 D8+i   FCOMP 0,i       compare 0 - i, pop
  330.  w  D8 D8+i   FCOMP i         compare 0 - i, pop
  331.  w  D8 /3     FCOMP mem4r     compare 0 - mem4r, pop
  332.  w  DC /3     FCOMP mem8r     compare 0 - mem8r, pop
  333.  w  DE D9     FCOMPP          compare 0 - 1, pop both
  334.  w  D9 FF     FCOS            387 only: push, 1/0 := cosine(old 0)
  335.  
  336.  w  D9 F6     FDECSTP         decrement stack pointer
  337.  w  DB E1     FDISI           disable interrupts (.287 ignore)
  338.  
  339.  w  DE F9     FDIV            1 := 1 / 0, pop
  340.  w  D8 F0+i   FDIV i          0 := 0 / i
  341.  w  DC F8+i   FDIV i,0        i := i / 0
  342.  w  D8 F0+i   FDIV 0,i        0 := 0 / i
  343.  w  D8 /6     FDIV mem4r      0 := 0 / mem4r
  344.  w  DC /6     FDIV mem8r      0 := 0 / mem8r
  345.  
  346.  w  DE F8+i   FDIVP i,0       i := i / 0, pop
  347.  w  DE F1     FDIVR           1 := 0 / 1, pop
  348.  w  D8 F8+i   FDIVR i         0 := i / 0
  349.  w  DC F0+i   FDIVR i,0       i := 0 / i
  350.  w  D8 F8+i   FDIVR 0,i       0 := i / 0
  351.  w  D8 /7     FDIVR mem4r     0 := mem4r / 0
  352.  w  DC /7     FDIVR mem8r     0 := mem8r / 0
  353.  w  DE F0+i   FDIVRP i,0      i := 0 / i, pop
  354.  
  355.  w  DB E0     FENI            enable interrupts (.287 ignore)
  356.  w  DD C0+i   FFREE i         empty i
  357.  w  DE /0     FIADD mem2i     0 := 0 + mem4i
  358.  w  DA /0     FIADD mem4i     0 := 0 + mem2i
  359.  w  DE /2     FICOM mem2i     compare 0 - mem2i
  360.  w  DA /2     FICOM mem4i     compare 0 - mem4i
  361.  w  DE /3     FICOMP mem2i    compare 0 - mem2i, pop
  362.  w  DA /3     FICOMP mem4i    compare 0 - mem4i, pop
  363.  
  364.  w  DE /6     FIDIV mem2i     0 := 0 / mem2i
  365.  w  DA /6     FIDIV mem4i     0 := 0 / mem4i
  366.  w  DE /7     FIDIVR mem2i    0 := mem2i / 0
  367.  w  DA /7     FIDIVR mem4i    0 := mem4i / 0
  368.  w  DF /0     FILD mem2i      push, 0 := mem2i
  369.  w  DB /0     FILD mem4i      push, 0 := mem4i
  370.  w  DF /5     FILD mem8i      push, 0 := mem8i
  371.                                                               7-8
  372.  
  373.  w  DE /1     FIMUL mem2i     0 := 0 * mem2i
  374.  w  DA /1     FIMUL mem4i     0 := 0 * mem4i
  375.  w  D9 F7     FINCSTP         increment stack pointer
  376. 9B  DB E3     FINIT           initialize 87
  377.  w  DF /2     FIST mem2i      mem2i := 0
  378.  w  DB /2     FIST mem4i      mem4i := 0
  379.  w  DF /3     FISTP mem2i     mem2i := 0, pop
  380.  w  DB /3     FISTP mem4i     mem4i := 0, pop
  381.  w  DF /7     FISTP mem8i     mem8i := 0, pop
  382.  
  383.  w  DE /4     FISUB mem2i     0 := 0 - mem2i
  384.  w  DA /4     FISUB mem4i     0 := 0 - mem4i
  385.  w  DE /5     FISUBR mem2i    0 := mem2i - 0
  386.  w  DA /5     FISUBR mem4i    0 := mem4i - 0
  387.  
  388.  
  389.  
  390.  w  D9 C0+i   FLD i           push, 0 := old i
  391.  w  DB /5     FLD mem10r      push, 0 := mem10r
  392.  w  D9 /0     FLD mem4r       push, 0 := mem4r
  393.  w  DD /0     FLD mem8r       push, 0 := mem8r
  394.  w  D9 E8     FLD1            push, 0 := 1.0
  395.  w  D9 /5     FLDCW mem2i     control word := mem2i
  396.  w  D9 /4     FLDENV mem14    environment := mem14
  397.  w  D9 EA     FLDL2E          push, 0 := log base 2.0 of e
  398.  w  D9 E9     FLDL2T          push, 0 := log base 2.0 of 10.0
  399.  w  D9 EC     FLDLG2          push, 0 := log base 10.0 of 2.0
  400.  w  D9 ED     FLDLN2          push, 0 := log base e of 2.0
  401.  w  D9 EB     FLDPI           push, 0 := Pi
  402.  w  D9 EE     FLDZ            push, 0 := +0.0
  403.  
  404.  w  DE C9     FMUL            1 := 1 * 0, pop
  405.  w  D8 C8+i   FMUL i          0 := 0 * i
  406.  w  DC C8+i   FMUL i,0        i := i * 0
  407.  w  D8 C8+i   FMUL 0,i        0 := 0 * i
  408.  w  D8 /1     FMUL mem4r      0 := 0 * mem4r
  409.  w  DC /1     FMUL mem8r      0 := 0 * mem8r
  410.  w  DE C8+i   FMULP i,0       i := i * 0, pop
  411.  
  412.     DB E2     FNCLEX          nowait clear exceptions
  413.     DB E1     FNDISI          disable interrupts (.287 ignore)
  414.     DB E0     FNENI           enable interrupts (.287 ignore)
  415.     DB E3     FNINIT          nowait initialize 87
  416.  w  D9 D0     FNOP            no operation
  417.  
  418.     DD /6     FNSAVE mem94    mem94 := 87 state
  419.     D9 /7     FNSTCW mem2i    mem2i := control word
  420.     D9 /6     FNSTENV mem14   mem14 := environment
  421.     DF E0     FNSTSW AX       AX := status word
  422.     DD /7     FNSTSW mem2i    mem2i := status word
  423.  w  D9 F3     FPATAN          0 := arctan(1/0), pop
  424.  w  D9 F8     FPREM           0 := REPEAT(0 - 1)
  425.  w  D9 F5     FPREM1          387 only: 0 := REPEAT(0 - 1) IEEE compat.
  426.  w  D9 F2     FPTAN           push, 1/0 := tan(old 0)
  427.                                                               7-9
  428.  
  429.  w  D9 FC     FRNDINT         0 := round(0)
  430.  w  DD /4     FRSTOR mem94    87 state := mem94
  431.  w  DD /6     FSAVE mem94     mem94 := 87 state
  432.  w  D9 FD     FSCALE          0 := 0 * 2.0 ** 1
  433. 9B  DB E4     FSETPM          set protection mode
  434.  w  D9 FE     FSIN            387 only: push, 1/0 := sine(old 0)
  435.  w  D9 FB     FSINCOS         387 only: push, 1 := sine, 0 := cos(old 0)
  436.  w  D9 FA     FSQRT           0 := square root of 0
  437.  
  438.  w  DD D0+i   FST i           i := 0
  439.  w  D9 /2     FST mem4r       mem4r := 0
  440.  w  DD /2     FST mem8r       mem8r := 0
  441.  w  D9 /7     FSTCW mem2i     mem2i := control word
  442.  w  D9 /6     FSTENV mem14    mem14 := environment
  443.  w  DD D8+i   FSTP i          i := 0, pop
  444.  w  DB /7     FSTP mem10r     mem10r := 0, pop
  445.  w  D9 /3     FSTP mem4r      mem4r := 0, pop
  446.  w  DD /3     FSTP mem8r      mem8r := 0, pop
  447.  w  DF E0     FSTSW AX        AX := status word
  448.  w  DD /7     FSTSW mem2i     mem2i := status word
  449.  
  450.  w  DE E9     FSUB            1 := 1 - 0, pop
  451.  w  D8 E0+i   FSUB i          0 := 0 - i
  452.  w  DC E8+i   FSUB i,0        i := i - 0
  453.  w  D8 E0+i   FSUB 0,i        0 := 0 - i
  454.  w  D8 /4     FSUB mem4r      0 := 0 - mem4r
  455.  w  DC /4     FSUB mem8r      0 := 0 - mem8r
  456.  w  DE E8+i   FSUBP i,0       i := i - 0, pop
  457.  w  DE E1     FSUBR           1 := 0 - 1, pop
  458.  w  D8 E8+i   FSUBR i         0 := i - 0
  459.  w  DC E0+i   FSUBR i,0       i := 0 - i
  460.  w  D8 E8+i   FSUBR 0,i       0 := i - 0
  461.  w  D8 /5     FSUBR mem4r     0 := mem4r - 0
  462.  w  DC /5     FSUBR mem8r     0 := mem8r - 0
  463.  w  DE E0+i   FSUBRP i,0      i := 0 - i, pop
  464.  
  465.  w  D9 E4     FTST            compare 0 - 0.0
  466.  w  DD E0+i   FUCOM i         387 only: unordered compare 0 - i
  467.  w  DD E1     FUCOM           387 only: unordered compare 0 - 1
  468.  w  DD E8+i   FUCOMP i        387 only: unordered compare 0 - i, pop
  469.  w  DD E9     FUCOMP          387 only: unordered compare 0 - 1, pop
  470.  w  DA E9     FUCOMPP         387 only: unordered compare 0 - 1, pop both
  471. 9B            FWAIT           wait for 87 ready
  472.  w  D9 E5     FXAM            C3 -- C0 := type of 0
  473.  w  D9 C9     FXCH            exchange 0 and 1
  474.  w  D9 C8+i   FXCH 0,i        exchange 0 and i
  475.  w  D9 C8+i   FXCH i          exchange 0 and i
  476.  w  D9 C8+i   FXCH i,0        exchange 0 and i
  477.  w  D9 F4     FXTRACT         push, 1 := expo, 0 := sig
  478.  w  D9 F1     FYL2X           0 := 1 * log base 2.0 of 0, pop
  479.  w  D9 F9     FYL2XP1         0 := 1 * log base 2.0 of (0+1.0), pop
  480.  
  481.