ASK MR. FORTH


by Donald Forbes

If the audiences at your FORTH demos ever wondered why a digital computer like Atari should be supported by a magazine whose unlikely name is embedded with dots, you can explain that it is short for Atari Newsletter And Lots Of Games.

Most of the games from the first ten issues have been collected in the ANALOG Compendium, which they can obtain for fifteen pieces of silver (actually $14.95 plus $2.00 postage and handling). For another thirty pieces of silver, they can mail the card enclosed in the Compendium, to get the games on six sides of three disks, and save themselves some typing.

The Compendium (an old ten-dollar word from the Latin for "that which is weighed together," now used to refer to a short, complete summary) also includes a dozen short programs in BASIC that show off Atari's graphic capabilities to good advantage.

The structure of some of these programs makes it easy to translate them to FORTH and thus show some of the similarities and differences between the two languages.

The first and shortest (Compendium, page 114) is a color demo in graphics 8 mode. Here is the BASIC code:

 5 REM GRAPHICS 8 COLOR DEMO
 10 GRAPHICS 8:SETCOLOR 2,0,15:SETCOLOR
  1,0,0:COLOR 1
 20 FOR X=0 TO 200 STEP 2
 30 PLOT X,0:DRAWTO X,10
 40 NEXT X
 50 FOR X=1 TO 201 STEP 2
 60 PLOT X,20:DRAWTO X,30
 70 NEXT X
 80 FOR X=0 TO 200
 90 PLOT X,40:DRAWTO X,50
 100 NEXT X

This program splits into four logical sections. We can mark these with a red pen and ruler after Lines 10, 40 and 70. Here is the line-for-line equivalent in FORTH:

      : GR8COLOR
        8 GR. 2 0 15 SETCOLOR
        1 0 0 SETCOLOR 1 COLOR
        201 0 DO
        I 0 PLOT I 10 DRAWTO
        2 +LOOP
        202 1 DO
        I 20 PLOT I 30 DRAWTO
        2 +LOOP
        201 0 DO
        I 40 PLOT I 50 DRAWTO
        LOOP ;

Note that the limit of the DO... LOOP in FORTH is one more than in BASIC, because FORTH quits when it reaches the limit. Furthermore, the colon definition is too long, even though it will work. FORTH is easier to understand and debug in small bites. An improvement would be to break the program into four pieces by line numbers, then combine them into one colon definition this way:

      : GR8COLOR
        LINE10 LINE20 LINE50 LINE80 :

The second program (Compendium, page 57) is a Graphics 11 GTIA demo.

 10 REM GRAPHICS 11 GTIA DEMO
 20 REM
 30 GRAPHICS 11
 40 CI=1:C=0:SETCOLOR 4,0,2
 50 FOR Y=0 TO 191
 60 FOR X=0 TO 79
 70 C=C+1:IF C=16 THEN C=0
 80 COLOR C
 90 PLOT X,Y
 100 NEXT X
 110 LC=LC+1:IF LC=16 THEN CI=-CI:LC=1
 120 C=C+CI:IF C=16 THEN C=0
 130 NEXT Y
 140 GOTO 140

In this program, there is a logical break after Line 40, and another after Line 130, with a DO ... LOOP in Lines 60 to 100 embedded in another DO ... LOOP.

Since the inner loop invokes the counter for the outer loop, we will need a definition of J (a FORTH-79 word that is not included in fig-FORTH, even though it's found in both Team Atari FORTH and valFORTH). The outer loop index Y in the BASIC program becomes J, and the inner index X becomes I.

Furthermore, the three variables CI, C and LC, which are defined automatically in BASIC, must be defined separately in FORTH. Here again is the line-for-line version:

      0 VARIABLE CI
      0 VARIABLE C
      0 VARIABLE LC
      : J R> R> R> R R# !
          >R >R >R   R# @ ;
      : GR11GTIA
        ( graphics 11 gtia demo )
        ( rem )
        11 GR.
        1 CI ! 0 C ! 4 0 2 SETCOLOR
HTMLer Note: The "CI" in the above line was printed as "CT" in the magazine; I believe it was a typo and altered it to what I perceive as correct.
        191 1 + 0 DO ( add 1 to limit)
         79 1 + 0 DO
        1 C +! C @ 16 = IF 0 C ! THEN
        C @ COLOR
        I J PLOT  ( X = I, Y = J)
        LOOP
        1 LC +! LC @ 16 = IF
          CI @ MINUS CI ! 1 LC ! THEN
        CI @ C +! C @ 16 = IF
          0 C ! THEN
        LOOP
        BEGIN 0 UNTIL ;

This code, too, calls for comments. In the first place, the colon definition is too long and should be broken up. Second, the endless loop 140 GOTO 140 can be handled in other ways. One way is a time-delay loop, or even an embedded loop:

   : DELAY 3 0 DO 30000 0 DO LOOP LOOP ;
or wait for a return key press with:
      : DELAY ." Press return"
        KEY DROP ;

Third, since we continuously need FORTH-79 words that are not in fig-FORTH, it makes sense to store them in a handy place. One way is to buy a set of alphabetic index tabs in a stationery store for about $3 and save the words in a loose-leaf binder. Obvious candidates are J, PICK, ROLL, random number generators, and double number extensions (most of which appear in Leo Scanlon's Forth Programming).

The third program is a Moire demo (Compendium, page 122). The BASIC code is:

 10 DEG
 20 A=INT(l.9*160)
 30 GRAPHICS 8+16
 40 SETCOLOR 2,0,0
 50 FOR I=0 TO 160 STEP 5
 60 B=INT(I/2)
 70 COLOR 1
 80 PLOT 0,B
 90 DRAWTO I,160
 100 PLOT A,B
 110 DRAWTO A-I,160
 120 PLOT 0,160-B
 130 DRAWTO I,0
 140 PLOT A,160-B
 150 DRAWTO A-I,0
 160 NEXT I
 170 IF PEEK(764)<>255 THEN END
 180 GOTO 170

This program has a beginning, a middle (the loop in Lines 50 to 160), and an end. In the FORTH translation, we need variables A and B. The DEG function is not necessary. The INT function isn't needed either, since we can use integer arithmetic. In Line 20, however, we must be careful to multiply 160 by 19 and then divide by 10.

      0 VARIABLE A
      0 VARIABLE B
      : MOIRE
        ( deg)
        160 19 l0 */ A !
        8 16 + GR.
        2 0 0 SETCOLOR
        160 1 + 0 DO
        I 2 / B !
        1 COLOR
        0 B @ PLOT
        I 160 DRAWTO
        A @ B @ PLOT
        A @ I - 160 DRAWTO
        0 160 B @ - PLOT
        I 0 DRAWTO
        A @ 160 B @ - PLOT
        A @ I - 0 DRAWTO
        5 +LOOP
        BEGIN 764 @ 255 = NOT IF
        ." quit" QUIT THEN
        0 UNTIL ;

This program, too, could be improved by breaking it into separate colon definitions, and by saving A and B on the stack. In the computer business, however, you learn early to be wary of the optimization trap: first, you make it work; then, if you still have time, you optimize.

The fourth program (Compendium, page 157) is called Pretty Demo, and it introduces some new features:

 10 DEG
 20 GRAPHICS 24
 30 COLOR 1
 40 SETCOLOR 2,0,0
 50 FOR I=1 TO 360 STEP 5
 60 X=319*1/360
 70 Y=80+80*SIN(I)
 80 IF I>270 THEN 100
 90 PLOT 0,0
 100 DRAWTO X,Y
 110 IF I<90 THEN 130
 120 DRAWTO 319,159
 130 NEXT I
 140 IF PEEK(764)<>255 THEN END
 150 GOTO 140

The first thing to notice is that Line 60 calls for us to multiply 319 by I, which eventually becomes 360, giving 114,840 as the product. This total exceeds the limit for signed integers, so we must use */ instead of a multiplication followed by a division.

Furthermore, in Line 70, we need a SIN function to compute the sine of the loop index - and FORTH does not provide a built-in SIN function.

The elegant solution is to develop a formula for a polynomial curve which will approximate the sine curve as closely as we need for our application. The simple way is to incorporate a table of sines in our program, with one entry for each degree from 0 to 90. Such a table appears on page 134 of Leo Scanlon's book and has the added advantage that we can compute other trigonometric functions, such as the cosine, by a simple transformation.

Here are the two screens needed to load the sine table:

      (t trig table screen 1)
      DECIMAL
      0 VARIABLE SINE
             0175 , 0349 , 0523 ,
      0698 , 0872 , 1045 , 1219 ,
      1392 , 1564 , 1736 , 1908 ,
      2079 , 2250 , 2419 , 2588 ,
      2756 , 2924 , 3090 , 3256 ,
      3420 , 3584 , 3746 , 3907 ,
      4067 , 4226 , 4384 , 4540 ,
      4695 , 4848 , 5000 , 5150 ,
      5299 , 5446 , 5592 , 5736 ,
      5878 , 6018 , 6157 , 6293 ,
      6428 , 6561 , 6691 , 6820 ,
      6947 , 7071 , 7193 , 7313 ,
      7431 ,

      ( trig table screen 2)
      7547 , 7660 , 7771 , 7880 ,
      7986 , 8090 , 8191 , 8290 ,
      8387 , 8480 , 8572 , 8660 ,
      8746 , 8829 , 8910 , 8988 ,
      9063 , 9135 , 9205 , 9272 ,
      9336 , 9397 , 9455 , 9511 ,
      9563 , 9613 , 9659 , 9703 ,
      9744 , 9781 , 9816 , 9848 ,
      9877 , 9903 , 9926 , 9945 ,
      9962 , 9976 , 9986 , 9994 ,
      9998 , 10000 ,
                   ;S

We need some code to reference the table, as in this screen:

      ( trig table screen 3)
      : LOOKUP SINE SWAP 2 * + @ ;
      : SIN
        DUP 270 >
        IF 360 SWAP - LOOKUP MINUS
        ELSE DUP 180 >
          IF 180 - LOOKUP MINUS
          ELSE DUP 90 >
             IF 180 SWAP - THEN
               LOOKUP
        THEN THEN
      : COS
        DUP 270 > IF 270 -
        ELSE 90 +
        THEN SIN ;        ;S

What SIN does is to return the sine of any integer-valued angle between 0 degrees and 360 degrees. To use the result, you must divide it by 10,000. As Scanlon points out, "the cosine of any given angle is equal to the sine of an angle that is 90 degrees greater."

He also notes that "negative angles have the same sines and cosines as their positive counterparts... This means you can also use SIN and COS for angles between -1 degrees and -360 degrees, by supplying the angle's absolute value on the stack."

Here, then, is the FORTH code for the Pretty Demo to match the BASIC line for line (except that we must remember to divide the sine value by 10000).

      0 VARIABLE X
      0 VARIABLE Y
      : PRETTY ( DEG )
        24 GR.
        1 COLOR
        2 0 0 SETCOLOR
        360 1 + 1 DO
        I 319 360 */ X !
        I SIN 80 10000 */ 80 + Y !
        I 270 < IF
        0 0 PLOT THEN
        X @ Y @ DRAWTO
        I 90 > IF
        319 159 DRAWTO THEN
        5 +LOOP
        BEGIN 764 @ 255 = NOT IF
        ." quit" QUIT THEN 0 UNTIL ;

Both sine and cosine functions can be combined in this short program from page 23 of the Compendium, called Circle Demo.

 10 XC=160: YC=80
 20 RD=60:INC=10:YS=0.75
 30 GRAPHICS 8:COLOR 1
 40 GOSUB 1000:END
 1000 REM ---------------------
 1010 REM CIRCLE DRAWER ROUTINE
 1020 REM ---------------------
 1030 REM
 1040 REM XC: x-coord. of center
 1050 REM YC: y-coord. of center
 1060 REM RD: circle radius
 1070 REM INC: drawing increment
 1080 REM YS: y-scaling factor
 1090 REM
 1100 DEG :PLOT XC,YC+RD*YS
 1110 FOR CIRCLE=0 TO 360 STEP INC
 1120 XCOORD=XC+SIN(CIRCLE)*RD
 1130 YCOORD=YC+COS(CIRCLE)*RD*YS
 1140 DRAWTO XCOORD,YCOORD
 1150 NEXT CIRCLE:RETURN

This program consists of a subroutine which calls a loop, which in turn draws the perimeter of the circle in steps of 10 degrees at a time, using the sine and cosine functions to locate the X and Y coordinates each time. Here is the FORTH version:

      ( circle demo 1)
      160 VARIABLE XC 80 VARIABLE YC
      10 VARIABLE INC 60 VARIABLE RD
      75 VARIABLE VS ( 0.75)
       0 VARIABLE XCOORD
       0 VARIABLE YCOORD
      : GOSUB1000 XC @ RD @ YS @ 100
        */ YC @ + PLOT
        360 1 + 0 DO
        I SIN RD @ 10000 */
          XC @ + XCOORD !
        I COS RD @ 10000 */
          YS @ 100 */
          YC @ + YCOORD !
        XCOORD @ YCOORD @ DRAWTO
        INC @ +LOOP ;      -->

      ( circle demo 2)
      : CIRCLE_DMNO 8 GR. I COLOR
        GOSUB1000 ;

Notice that the program contains a scaling factor (0.75) that was set at 75 in the FORTH program and that later must be divided by 100. Furthermore, both the sine and cosine values must first be divided by 10,000. if the output looks more like an egg than a circle, you may, want to experiment with the scaling factor.

You can write a program to draw circles without using sines and cosines. There is a fiendishly clever program to do just that on page 125 of the Compendium, and it takes only twenty-five short lines. This innocent-looking program with the simple title Circle Radius Demo looks easy:

 10 XCENTER=310/2:YCENTER=192/2
 100 GRAPHICS 8
 110 COLOR 1
 120 ? "ENTER RADIUS:";:INPUT RADIUS
 130 LET RADIUS=RADIUS+3-1
 140 LET X=0
 150 LET Y=RADIUS
 160 LET DIAMETER=3-2*RADIUS
 170 IF X<=Y THEN GOSUB 1000: IF DIAMET
 ER<0 THEN DIAMETER=DIAMETER+4*X+6:X=X+
 1:GOTO 170
 180 IF X>Y THEN END
 190 DIAMETER=DIAMETER+4*(X-Y)+10
 200 Y=Y-1
 210 X=X+1:GOTO 170
 1000 REM
 1010 PLOT XCENTER+X,YCENTER+Y
 1020 PLOT XCENTER+Y,YCENTER+X
 1030 PLOT XCENTER+Y,YCENTER-X
 1040 PLOT XCENTER+X,YCENTER-Y
 1050 PLOT XCENTER-X,YCENTER-Y
 1060 PLOT XCENTER-Y,YCENTER-X
 1070 PLOT XCENTER-Y,YCENTER+X
 1080 PLOT XCENTER-X,YCENTER+Y
 1090 RETURN

The structure of the program appears to be straightforward. You draw a red line after Line 160 and another after Line 210. Lines 10 through 160 are just sequential code, and all that is different is Line 120. It asks for input from the keyboard, which should present no problem in FORTH.

The subroutine at the end of the program is also clear-cut. It is only when you begin to translate the five statements in Lines 170 to 210 that you realize you've hit a booby trap. This is what is referred to as "spaghetti code" (IBM's Joan K. Hughes, in her book PL/1 Structured Programming called it "bowl-of-spaghetti code' or BS code), and the tip-off is right there: two GOTO 170 statements, one unconditional and one nested inside an IF statement.

Computer science advanced in the 1970s from a black art to an organized and systematic process, when the mischief of the GOTO statement was finally identified. Newer languages, such as PL/1, found substitutes, and Pascal banished it completely.

Structured programming at last made it possible to write programs that were free of logical errors and were relatively easy to debug and maintain.

Computer scientists demonstrated mathematically that any program could be built from a set of three simple building blocks with a common property: one input and one output. The SEQUENCE block has the trivial structure of one process performed after another. The IFTHENELSE block is merely a two-way branch. The third is the DOWHILE block, which tests for a true condition and then repeats an operation, as long as the test remains true (to exit from the block, the operation itself must reset the flag).

There are two additional variations in common use. The IFTHENELSE block can sometimes be replaced by the SELECT block (or CASE statement), which features a multiple-branch fork to avoid an awkward set of nested IF statements. A payroll program, for instance, could test immediately for single, married, widowed, divorced, separated, or never married.

The variation on the DOWHILE block is the DOUNTIL block which places the logical test at the end, instead of the beginning - and thus creates a hidden trap. The loop will always be executed at least once, as you'll discover to your consternation, when your payroll program looks for end-of-file after the first record, but the operator mounts a tape that has only a header label and a trailer label, and no first record!

The details are given in Top Down Structured Programming Techniques by C. McGowan and J. Kelly, Petrochelli/Charter, New York, 1975. If your library doesn't have this, they can borrow it from another library. Anyone in your audience who writes programs for a living will appreciate the tip.

The logical structure of our spaghetti code, as the FORTH translation makes clear, is a pair of nested DOWHILE loops, with this structure in FORTH:

      BEGIN condition WHILE
          FORTH words .....
      REPEAT
The first condition tested is whether X is less than or equal to Y, the second is whether DIAMETER is less than 0, or negative.

You can ask for a number from the keyboard with the sequence QUERY ?TERMINAL INTERPRET. It will substitute for the INPUT statement in BASIC, and you may want to file it for reference.

The FORTH code below also takes some liberties with the BASIC code. The END has been replaced by . "quit" QUIT. By using INITIAL to reset the variables, shifting 8 GR., and then adding RERUN, you can use RADIUS_DEMO to draw the first circle, and then RERUN to draw more circles without clearing the screen.

      ( radius 1)
      0 VARIABLE XCENTER
      0 VARIABLE YCENTER
      0 VARIABLE XX 0 VARIABLE YY
      0 VARIABLE DIAMETER
      0 VARIABLE RADIUS ( < 100 )
      : INPUTU ." radius? " QUERY CR
        ?TERMINAL CR INTERPRET
        SWAP DROP ;
      : INITIAL INPUT# RADIUS !
        155 XCENTER ! 96 YCENTER !
        0 XX ! 0 YY ! 0 DIAMETER !
        ( 8 GR. ) 1 COLOR
        2 RADIUS +! 0 XX
        RADIUS @ YY ! 3 RADIUS @
        2 * - DIAMETER ! ;      -->

      ( radius 2)
      : GOSUB1000
        XCENTER @ XX @ +
        YCENTER @ YY @ + PLOT
        XCENTER @ YY @ +
        YCENTER @ XX @ + PLOT
        XCENTER @ YY @ +
        YCENTER @ XX @ - PLOT
        XCENTER @ XX @ +
        YCENTER @ YY @ - PLOT  -->

      ( radius 3)
        XCENTER @ XX @ -
        YCENTER @ YY @ - PLOT
        XCENTER @ YY @ -
        YCENTER @ XX @ - PLOT
        XCENTER @ YY @ -
        YCENTER @ XX @ + PLOT
        XCENTER @ XX @ -
        YCENTER @ YY @ + PLOT ; -->

      ( radius 4)
      : L170
        BEGIN XX @ YY @ > NOT WHILE
        GOSUB1000
         BEGIN DIAMETER @ 0< WHILE
         XX @ 4 * 6 + DIAMETER +!
         1 XX +! REPEAT
        XX @ YY @ - 4 * 10 +
         DIAMETER +!
         -1 YY +! 1 XX +! REPEAT
        ." quit " QUIT ;
      : RADIUS_DENO 8 GR.
        INITIAL L170 ;
      : RERUN INITIAL L170 ;    ;S

The ANALOG Compendium has half a dozen other short, graphic BASIC programs (especially the Triangle on page 29) which appear to be likely candidates for FORTH translations.

Next month you'll have an opportunity to upgrade the show-and-tell sessions you've conducted to date, and become a full fledged professor - by teaching a FORTH-79 class to beginners.

You will need one copy of the textbook for the class, which you may want to order right away (if you can't borrow a copy). The book costs $16, so be prepared to pass the hat at this session and the next.

The text is The Complete Forth by Alan Winfield, Wiley Press, 605 Third Avenue, New York, New York 10158. You may find it at bookstores like Dalton's and Walden Books, or have your bookseller order one for you. Finally, you may charge to a credit card by telephoning Mountain View Press (P.O. Box 4656, Mountain View, California 94040) at 415-961-4103.

If your audience has enjoyed your demos to date, they'll find your next session even more rewarding.


Previous | Next

Original text copyright 1984 by ANALOG Computing. Reprinted with permission by the Digital ANALOG Archive.