The Forth Column

Martin offers some thoughts on the state of real-time Forth, as well as source code for floating-point extensions.


June 01, 1988
URL:http://www.drdobbs.com/database/the-forth-column/184407960

Figure 1

Figure 1

JUN88: THE FORTH COLUMN

THE FORTH COLUMN

Real-Time Tidbits and some Words on Floating Point

Martin Tracy

"Using a real-time operating system to encase your application is like wearing armor into battle. The armored knight was better protected than an unarmored warrior. But the extra weight he was carrying also made him slower and less agile." So writes Charles H. Small in his article "Real-Time Operating Systems" (EDN, January 7, 1988). Speaking further on reentrancy, Mr. Small reminds his readers that "Some languages, such as Forth, produce inherently reentrant code. Other languages require discipline on the part of the programmer and a special compiler that produces ROMable code." (It's easy to keep Forth code reentrant. Just keep arguments and parameters on the stack or in USER variables, and keep strings at PAD.)

Mr. Small then goes into a thoughtful and detailed description of Forth Inc.'s polyFORTH multitasker. This much copied round-robin multitasker is characterized by its PAUSE command. PAUSE is every task's entry into a circularly linked queue, which eventually surfaces to execute the next awake task. (Tasks are generally asleep until awoken by an interrupt.) Each task retains control of the CPU for as long as it wishes, handing it off to the next task when convenient. This is apparently also the model for the Laxen/Perry public-domain F83, although nobody, but nobody, knows as much about round-robin multitasking as FORTH Inc. does. I should also like to point out to speed freaks everywhere that the time required to put one task to sleep, traverse the round robin, and wake the next task (that is, the taskswitching time) on a DSP polyFORTH running on a TM532020 chip is less than 2 microseconds.

Curiously, an article by Max Schindler called "Toward Faster and Portable Real-Time Operating Systems" in Electronic Design (January 21, 1988) does not even mention Forth, even though this same issue contains "Combine Forth with Other Tools for Rapid Software Development" by W. H. Payne.

The Proceedings of the 1987 Rochester Forth Conference (reviewed here in the October 1987 issue of DDJ) are now available as vol. 5, no. 1, of the Journal of Forth Application and Research (JFAR). The 256 pages contain more than 50 papers on comparative computer architectures and many other topics. The subscription rate is $50 per year and back issues are $15 each. Call 716-254-0621 and ask for Forth.

Drs. Lou Odette and William B. Dress, well known to both the Forth and the Al communities, sent in a copy of their jointly written "Engineering Intelligence into Real-Time Applications" from the November 1987 Expert Systems (vol. 4, no. 4). The article focuses on the performance of knowledge-based, intermediate-size computer systems, such as you might expect to find in self-testing and self-calibrating machinery for manufacturing or monitoring. The authors present methodology to "effect the transfer of knowledge-base technology to real-time systems while meeting the constraints of embedded applications." They present an interesting graph of hardware and software choices, which I have attempted to duplicate in Figure 1, page 91.

The authors' approach is to construct a compiler for OPS/Prolog that emits compact code for a highly portable, threaded-code intermediate language (Forth). The code, in turn, is used to implement the abstract machine underlying OPS5 and Prolog. Its first real-time application will be to control the Microgravity Vestibular Investigation, designed for experiments in space motion sickness aboard the Spacelab (currently scheduled for April 1991). The bibliography presented at the end of the article is worth an article in itself.

Call for Presentations

Get out your calendars and mark off November 18 and 19 for the 1988 Forth Convention at the Grand Hotel in Anaheim, California. This year's theme is real-time, real-world programming. If you ever needed an excuse to bring your family to see Disneyland, this is it.

The convention is sponsored by the Forth Interest Group (FIG). We need presentations (not papers) on real-time programming and related topics, such as language-oriented RISC machines, working neural nets, Forth in aerospace, et cetera. Just send a short abstract to FIG, P.O. Box 8231, San Jose, CA 95155, or call 408-277-0668. We also need volunteers to chair and participate in panel discussions.

Come meet your vendor, or find your local FIG group, or hear Chuck Moore's fireside chat, or watch a contest to find the world's fastest programmer, or. ... Don't miss this exciting event! I'll remind you about it again later.

The Third ANSI X3J14 Meeting

The third meeting of the X3J14 ANS Forth Technical Committee was held at Redondo Beach, California, on February 10-12, 1988. You can download Ray Duncan's brief report on the meeting from almost any Forth BBS (try the ECFB or GENie). But first, a brief review of the Standard process so far.

According to Ray, "At its first meeting, the X3J14 Technical Committee adopted the text of the Forth-83 Standard (less the Experimental Proposals) as its working, or BASIS, document. The BASIS will evolve into the draft proposed American National Standard (dpANS) document as the TC [Technical Committee] ratifies Technical Proposals which delete, amend, or enlarge the BASIS."

At the second meeting, the only significant change to the BASIS, other than reformatting it, was to relax the restriction on 16-bit stacks and addresses and allow Standard systems with other word sizes. The technical highlights of the third meeting included:

    1. Removing the requirement for floored division while still making it available for existing programs. In effect, division by negative numbers yields an implementation-dependent quotient and remainder, which satisfy the equation that quotient times dividend plus remainder equals divisor.

    2. Adding NIP and TUCK to the Controlled Reference Word Set.

    3. Adding EVAL, a word that allows the interpretation of arbitrary strings. (Remember, you saw it first here.) To quote Ray Duncan again, "When EVAL is available, words that redirect the input stream can be readily ported from one system to another, and new interpreters can be easily built in an implementation-independent manner."

Although not yet passed, and much to everyone's surprise, a Technical Proposal for a minimum extension set of floating-point operators was scheduled for fine-tuning and possible action at a later meeting. So, in honor of this commitment, I will direct the technical content of this month's column accordingly.

Floating Point

Conservative Forth programmers disdain floating-point for higher-performance, fixed-point arithmetic. "If you need to use floating-point arithmetic," they say, "then you don't understand the problem." Nevertheless, virtually all major Forth vendors now offer a floating-point packaged extension. Most of these extensions closely follow the hardware or firmware floating-point support on the host system, such as the 8087 coprocessor or the Macintosh SANE interface. Software-only, floating-point implementations usually follow the guidelines in "The FVG Standard Floating-Point Extension" by Duncan and Tracy (DDJ, September 1984).

Figure 1hardware and software choices

Virtually all floating-point (FP) extensions provide the four functions named F+, F-, F*, and F/. But what stack are the arguments on? Hardware implementations favor the separate FP stack provided by the hardware. Software implementations favor the efficiency of having everything on the normal (data) stack. Judging by the discussions at the last ANS Forth meeting, the separate FP stack wins hands down. Still, you can have your cake and eat it, too. Imagine a definition of F+ such as the following in a software FP system:

  F+ [FPOP] (add numbers here) [FPUSH];

In a Standard program, [FPOP] would pop an FP number from the FP stack and push it on the data stack. IFPUSH] would have the reverse action. Nonstandard programs that required maximum speed would simply redefine [FPOP] and [FPUSH] before compiling the same FP extension:

: [FPOP] IMMEDIATE

:[FPUSH] IMMEDIATE

The existence of a separate FP stack implies the existence of FP stack operators, and most FP packages supply FDUP, FDROP, FSWAP, FOVER, and FROT. In some packages, F@ and F! move FP numbers to and from FVARIABLEs, and FCONSTANTs such as PI aid readability. FP numbers can be compared with F< and F>. F= may be provided, but comparing floating-point numbers for equality is a questionable practice and should be avoided. It is meaningful, however, to compare an FP number to 0 with FO= or FO<. FABS, FNEGATE, FMAX, and FMIN are usually also available.

How to input an FP (real) number varies from Forth to Forth. Most Forths install some kind of automatic conversion to FP when the FP extension is compiled. In polyFORTH, numbers containing a period and followed by at least one nonblank character are converted to FP-for example, 2.0E would be a "real" 2 whereas 2. would be a double-precision 2.

In MasterFORTH, UR/FORTH, and MacFORTH, all derivatives of the FVG Standard, real numbers must contain an uppercase E-EE, or 2.E, or 2.0E, or 2E0, and so on.

You must be in DECIMAL or strange things happen. To print a real number, use F., but first set the number of places to the right of the decimal with PLACES:

4 PLACES 3.14159E F.

prints 3.1416.

The polyFORTH F. is an exception to this rule and takes the number of places from the stack. If you are a polyFORTH user and would like compatibility with the FVG Standard, redefine F. in this way:

VARIABLE Places
: PLACES ( n ) Places !

: F. ( r ) Places @ F.

Let's stop here for now. You can see that without much effort the floating-point packages of the various Forth vendors could be brought into alignment, and this is one of the things the ANS Forth committee is trying to do.

Now let's see what's involved in writing a floating-point package, in case you would like to experiment with it. First, you need a proper representation. The most efficient representation for Forth seems to be to use a normalized, double-precision signed mantissa and a single-precision signed exponent with the exponent on top. This gives you about nine digits of precision and an unheard of dynamic range. The high bit of the mantissa is the sign bit. The remaining bits form a normalized mantissa, unsigned, with the second highest bit set to 1. The binary point is to the left of this bit. This gives the mantissa 31 bits of precision. The number 1 would be:

HEX 0000 4000 1 DECIMAL

or "one-half times two to the one power."

To multiply and divide these essentially double-precision numbers, you're going to need some extended math operators, shown in Table 1, below.

Table 1: Extended math operators

Forth Word                        Meaning
---------------------------------------------------------------------

D>SHIFT (d u - d2)             Double-shift d] ['u bits to the right arithmetically

DU2/ (d - d2)                     Unsigned double-precision right shift
T + (tA tB - tC)                  Add two triple numbers
T2* (t - t2)                      Shift triple-precision number left once
TU2/ (t - ut)                     Signed triple-precision number right shift
Q2* (q - q')                      Shift quad-precision number left once
DUM* (ud1 ud2 - uqproduct)     Multiply unsigned double-numbers to yield unsigned result

DUM (uq ud - udquotient)

DUM*/ (ud ud2 ud3 - ud4)       Divide unsigned quad uq by unigned dividend ud (ud * ud2)/ud3
with quad-               precision intermediate

Writing the various stack-manipulation and memory-access words is also straightforward: FDUP, FDROP, FSWAP, FOVER, FROT, F@, F!, F,, FCONSTANT, FVARIABLE, and FLITERAL.

Rather than support a fancy system of infinities and "not-a-numbers," the only special case I will handle is 0. A zero number will have a zero mantissa, which can be easily tested with the expression OVER 0=, as follows:

Example 1: Normalizing and rounding the quad-precision result


       :   QNORM ( q - t exp)
       /   normalize q to bit 30;
       /   leave adjustment as exp.
           2DUP OR 2OVER OR OR
           IF 1 ( count ) >R
            BEGIN DUP 0< NOT
                WHILE Q2* R> 2- >R REPEAT
            2>R SWAP DROP 2R> TU2/ R>
           THEN ;
       :   ROUND ( t - ud exp )
       /   assumes hi bit is zero.
           32768 0 0 T+ ROT DROP
           DUP 0< DUP IF >R DU2/ r> THEN ;

       :   F* ( r r2 - r3 )
           f0= IF FSWAP THEN
           FOVER F0= IF FDROP EXIT THEN
           ( exp2 ) >R ROT ( exp ) >R UNPACK >R
           2SWAP UNPACK >R DUM*
           QNORM ( t exp ) >R
           ROUND ( d exp ) R> + ROT ROT
           2R> XOR PACK ROT SR> + + 1+ ;

: f0= (r-rf) OVER 0= : F0= (r - f) ROT 2DROP 0=

Changing the sign of the mantissa is easy but you must not change the sign of 0:

 : FNEGATE ( r - r2) \ 2's complement of R.
 OVER IF >R [ HEX ] 8000     [DECIMAL ] XOR
 R> THEN;
 : FABS ( r - r2) \ absolute value of R.
 >R [ HEX] 7FFF [ DECIMAL ] AND R> ;

Packing and unpacking the sign bit from the mantissa is also easy:

: UNPACK  ( d - ud sign)     DUP FABS ;
PACK ( ud sign - d)     [HEX] 8000 [DECIMAL] AND OR;

Probably the simplest function to implement is F* because exponents add and mantissas multiply. You do, however, need some way to normalize and round the quad-precision result, as shown in Example 1, page 94.

Example 2: Input and output primitives

      :   F. OVER >R FABS DUP 0>
          IF   0 0 ROT 0
               DO Q2 LOOP Q2* 999999999. DMIN
                    2SWAP DU2/
          ELSE NEGATE D>SHIFT 0 0 2SWAP THEN
          5000000000. 1073741824. DUM*/
          <# # # # # # # # #
          [ ASCII . ] LITERAL HOLD 2DROP
          # S R> 0< SIGN #> TYPE SPACE ;

      :   FLOAT ( d - r )
          SDUP D0= IF 0 EXIT TEHN
          SWAP OVER DABS 1 ( count ) >R
          BEGIN DUP 0< NOT
          WHILE D2* R> 1- >R REPEAT
          DU2/ ROT PACK R> 31 + ;

      :   >F ( d - fn )
      \   converts most recent double number
      \   to mixed fraction.
      \   Used like 3.14159 >F
          DPL @ ?DUP
          IF 1 0 ROT 0
               DO 10 0 DUM* 2DROP LOOP
               FLOAT F/
          THEN ;

          3.14159 FLOAT F. prints 3.1418999
          3.14159 FLOAT FCONSTANT PI
          PI PI F* F. prints 9.869587719


The other arithmetic primitives F +, F-, and F/-follow a similar pattern. For a simple four-function pack. age, you lack only the primitives for input and output, shown in Example 2, this page. >F converts a double to a real number, and F. prints it. This simple version of F. clips the real number to only a part of its dynamic range, limiting it to 999999999. Numbers less than 0.000000001 are printed as 0. A more sophisticated version would use logarithms to change the number from a power of 2 to a power of 10 before printing.

Forth Column by Martin Tracy

Example 1: Normalizing and rounding the quad-precision result

: QNORM ( q - t exp)
\ normalize q to bit 30;
\ leave adjustment as exp.
  2DUP OR 2OVER OR OR
  IF 1 ( count) >R
   BEGIN DUP 0< NOT
    WHILE Q2* R> 1- >R REPEAT
   2>R SWAP DROP 2R> TU2/ R>
  THEN ;

: ROUND ( t - ud exp )
\ assumes hi bit is zero.
  32768 0 0 T+ ROT DROP
  DUP 0< DUP IF >R DU2/ R> THEN ;

: F* ( r r2 - r3)
  f0= IF FSWAP THEN
  FOVER F0= IF FDROP EXIT THEN
  ( exp2 ) >R ROT ( exp ) >R  UNPACK >R
  2SWAP UNPACK >R DUM*
  QNORM ( t exp ) >R
  ROUND ( d exp ) R> + ROT ROT
  2R> XOR PACK ROT 2R> + + 1+ ;




Example 2: Input and output primitives

: F. OVER >R FABS DUP 0> IF 0 0 ROT 0 DO Q2* LOOP Q2* 999999999. DMIN 2SWAP DU2/ ELSE NEGATE D>SHIFT 0 0 2SWAP THEN 500000000. 1073741824. DUM*/ <# # # # # # # # # # [ ASCII . ] LITERAL HOLD 2DROP #S R> 0< SIGN #> TYPE SPACE ; : FLOAT ( d - r) 2DUP D0= IF 0 EXIT THEN SWAP OVER DABS 1 ( count) >R BEGIN DUP 0< NOT WHILE D2* R> 1- >R REPEAT DU2/ ROT PACK R> 31 + ; : >F ( d - fn) \ converts most recent double number \ to mixed fraction. \ Used like 3.14159 >F DPL @ 0< ABORT" Needs dec point" FLOAT DPL @ ?DUP IF 1 0 ROT 0 DO 10 0 DUM* 2DROP LOOP FLOAT F/ THEN ; 3.14159 FLOAT F. prints 3.141589999 3.14159 FLOAT FCONSTANT PI PI PI F* F. prints 9.869587719

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.