A.N.A.L.O.G. ISSUE 13 / SEPTEMBER 1983 / PAGE 91
We’d like to welcome Sally Forth to the pages of A.N.A.L.O.G. Computing. Her new FORTH column will help to familiarize our readers with the power and versatility of this popular programming language.
This column assumes some prior familiarity with FORTH. For a general introduction, visit your local library or computer bookstore and get a copy of Starting FORTH by Leo Brodie (published by Prentice-Hall) and/or Discover FORTH by Thom Hogan (McGraw-Hill).
Sally welcomes your questions about FORTH and will publish the most interesting letters in future issues. Write to her in care of A.N.A.L.O.G. Computing Magazine, 565 Main Street, Cherry Valley, MA 01611
I’d like to write some games on my ATARI, but BASIC is just too slow. My friends tell me that FORTH is almost as fast as machine language, and a lot easier to write, too. What’s the scoop?
Good question, Freak. Although FORTH is indeed a very fast programming language, it’s hard to make broad generalizations about its speed compared to other languages. Let’s try a few experiments to see how ATARI BASIC, FORTH and machine language really stack up.
Listing 1 is an ATARI BASIC program that uses nested FOR/NEXT loops and direct POKEs to fill a GRAPHICS 24 hi-res screen with color. The inner “J” loop (lines 240–260) fills up 6 scan lines or 240 bytes at a time. The outer “I” loop executes the “J” loop 32 times; 32 times 240 is 7680, the number of bytes in a GRAPHICS 24 screen.
The system timers at locations 19 and 20 decimal are used to clock the execution of the fill-loop in 60ths of a second or “jiffies.” The operating system increments location 20 once every jiffy; after 256 jiffies (4.23 seconds), location 20 is reset to zero and location 19 is incremented. Our BASIC program resets both counters by POKEing them with zeros before it begins filling the screen. After the fill is complete, PEEK(19) is multiplied by 256 and added to PEEK(20) to obtain the execution time.
Listing 1. 100 REM **************************** 110 REM * ATARI/FORTH/ML BENCHHARK * 120 REM * BY SALLY FORTH * 130 REM **************************** 140 GRAPHICS 24 150 REM 160 REM * ZERO SYSTEM JIFFY TIMERS 170 POKE 19,0:POKE 20,8 180 REM 190 REM * FIND ADDRESS OF SCREEN 200 SCREEN=PEEK(88)+256*PEEK(89) 210 REM 220 REM # SCREEN-FILL LOOP 230 FOR I=1 TO 32 240 FOR J=0 TO 239 250 POKE SCREEN+J,255 260 NEXT J 270 SCREEN=SCREEN+240 280 NEXT I 290 REM 300 REM * DETERMINE FILL TIME 310 JIFFIES=PEEK(20)+256*PEEK(19) 320 REM 330 REM * DISPLAY FILL TINE 340 GRAPHICS 350 PRINT JIFFIES;" JIFFIES":END
Notice that the GRAPHICS statements on lines 140 and 340 are executed outside the timed loop. This was done because GRAPHICS calls are performed by the CIO (Central Input/Output) utility in the OS, not by the BASIC cartridge. Any language environment that uses CIO to access the screen will require the same amount of execution time, so it’s fairer to leave GRAPHICS calls out of our benchmark test.
Listing 2A is a BASIC program that uses a machine-language subroutine to fill a GRAPHICS 24 screen. The 45 bytes of machine-language code in DATA lines 330–340 are converted into a string (ML$); after setting up GRAPHICS 24, the routine is activated by the USR call in line 260. Listing 2B is the source code for the DATA statements, created with the MAC/65 Macro Assembler. I structured the machine code to work in a manner similar to the pure BASIC version.
Listing 2A. 180 REM **************************** lie REM * BASIC/ML/FORTH BENCHMARK * 120 REM * ASSENBLY DEMO FROM BASIC * 130 REM * BY SALLY FORTH * 140 REM **************************** 150 REM 160 REN * READ ML DATA INTO ML$ 170 DIM ML$(45) 180 FOR I=1 TO 45 190 READ BYTE:ML$(I)=CHR$(BYTE) 200 NEXT I 210 REM 220 REN * SET UP GRAPHICS MODE 230 GRAPHICS 24 240 REM 250 REM * FILL SCREEN, GET TIMING 260 JIFFIES=USR(ADR(ML$)) 270 REM 280 REM * DISPLAY TINER READING 290 GRAPHICS 0 300 PRINT JIFFIES;" JIFFIES":END 310 REM 320 REM * DATA FOR ML ROUTINE 330 DATA 104,169,0,133,19,133,20,162,3 2,160,0,169,255,145,88,200,192,240,288 ,249,24,165,88,105 340 DATA 240,133,88,165,89,105,0,133,8 9,202,208,229,165,19,133,213,165,20,13 3,212,96
Listing 2B. 0100 ; **************************** 0110 ; * BASIC/FORTH/ML BENCHMARK * 0120 ; * ASSEMBLY VERSION * 0130 ; * BY SALLY FORTH * 0140 ; **************************** 0150 ; 0160 ; System equates 0170 ; 0180 SAVMSC = $58 ; Addr of screen 0190 BASRET = $D4 ; BASIC return 0200 RTCLOK = $13 ; System timer 0210 ; 0220 ; For simplicity, we'll let 0230 ; BASIC set up GRAPHICS 24. 0240 ; 0250 PLA ; # args; ignore 0260 ; 0270 ; Zero the system timers 0280 ; 0290 LDA #0 0300 STA RTCLOK 0310 STA RTCLOK+1 0320 ; 0330 ; This is the screen-fill loop 0340 ; 0350 LDX #32 ; Loop 32 times 0360 NEXTI 0370 LDX #0 ; Init J-loop 0380 LDA #255 ; Fill byte 0390 NEXTJ 0400 STA (SAVMSC),Y ; Poke it! 0410 INY 0420 CPY #240 ; 240 pokes yet? 0430 BNE NEXTJ ; No; do another 0440 CLC 0450 LDA SAVMSC ; Add 240 to 0460 ADC #240 ; screen addr 0470 STA SAVMSC 0480 LDA SAVMSC+1 ; This is a 0490 ADC #0 ; 16-bit add 0500 STA SAVMSC+1 0510 DEX ; 32 loops yet? 0520 BNE NEXTI ; No; do another 0530 ; 0540 ; Screen is now filled, so pass 0550 ; the timer readings to BASIC 0560 ; 0570 LDA RTCLOK 0580 STA BASRET+1 0590 LDA RTCLOK+1 0600 STA BASRET 0610 RTS ; Back to BASIC!
Listing 3. Scr # 1 0 ( BASIC/ML/FORTH BENCHNARK ) 1 ( FORTH VERSION BY SALLY FORTH ) 2 3 0 VARIABLE READING 4 5 88 CONSTANT SCREEN 6 7 : SCREENFILL ( THE FILL LOOP ) 8 32 0 DO 9 240 0 DO 10 255 I SCREEN @ + C! 11 LOOP 12 240 SCREEN @ + SCREEN ! 13 LOOP ; 14 15 --> Scr # 2 0 ( BENCHMARK CONTINUED 1 2 : BENCHMARK 3 24 GR. 4 0 19 ! ( ZERO TIMERS ) 5 SCREENFILL 6 256 19 C@ * 20 C@ + 7 READING ! ( SAVE READING ) 8 0 GR. 9 READING @ . ( SHOW READING ) 10 ." JIFFIES" CR ; 11 12 13 14 15
Listing 3 shows one way to implement the screen-fill routine in FORTH. Again, the program is deliberately organized to work like the BASIC program.
I ran the BASIC, machine-language and FORTH benchmarks on my 48K 800 system and obtained the results shown in Listing 4. BASIC is painfully slow, requiring well over a minute to POKE the screen full of bytes. The FORTH code runs in just under four seconds, an encouraging 18-times speed increase over BASIC. But the machine language code flies past in just five jiffies! This is 47 times faster than the FORTH demo, and over 863 times faster than the BASIC cartridge! Machine language is the clear winner in this example.
The above demonstration should not be taken as the definitive comparison of BASIC, FORTH and machine code. Before you pawn your FORTH system to buy a macro assembler, consider the next paragraphs.
The BASIC program in Listing 1 is a very inefficient way to fill a GRAPHICS 24 screen. Those nested FOR/NEXT loops make the speed comparison more revealing, but they really put a strain on the poor old cartridge. By replacing the double loop with a single mega-loop, and combining many statements into a single line, we can realize a small but useful speed improvement. Want proof? Type in and RUN the program in Listing 5. You should obtain an execution time of 3243 jiffies or about 54 seconds. It’s still pretty sluggish, but anything is better than Listing 1.
Listing 5. 100 GRAPHICS 24 110 POKE 19,0;POKE 20,0:SCREEN=PEEK(88 )+256*PEEK(89) 120 FOR I=SCREEN TO SCREEN+7688:POKE I ,255:NEXT I:JIFFIES=PEEK(20)+256*PEEK( 19) 130 GRAPHICS 0 140 PRINT JIFFIES;" JIFFIES":END
Now for some real eye-popping magic. Type and RUN the program in Listing 6, and watch ATARI BASIC fill an entire GRAPHICS 24 screen in only 12 jiffies—without using the USR function! The secret is in line 120, where we DIMension a string (S$) equal to the size of the screen. Next, we go POKEing around inside BASIC’s variable table, fooling the cartridge into thinking that the string occupies the same memory area as the screen. Finally, we use BASIC string manipulations in lines 200–220 to rapidly fill the screen/string with bytes.
Listing 6. 100 GRAPHICS 24 110 POKE 19,0:POKE 20,0 120 DIM S$(7680) 130 STRING=PEEK(140)+256*PEEK(141) 140 SCREEN=PEEK(88)+256*PEEK(89) 150 TABLE=PEEK(134)+256*PEEK(135) 160 OFFSET=SCREEN-STRING 170 HI=INT(OFFSET/256) 180 LO=OFFSET-(HI*256) 190 POKE TABLE+2,LO:POKE TABLE+3,HI 200 S$(1,1)=CHR$(255) 210 S$(7680,7680)=CHR$(255) 220 S$(2)=S$ 230 JIFFIES=PEEK(20)+256*PEEK(19) 240 GRAPHICS 0 250 PRINT JIFFIES;" JIFFIES":END
FORTH fanatics may be looking at Listing 6 with dismay. But fear not, stackers, FORTH can still show ATARI BASIC a thing or two! Instead of using the clumsy DO loops in Listing 3, we can use the standard FORTH word FILL to cover a GRAPHICS 24 screen with lightning speed. Compile Listing 7 into your FORTH system, and enjoy a very respectable execution time of only 7 jiffies—again, without any direct calls to machine language.
By studying the material presented here, we can infer the following points about BASIC, FORTH and programming in general:
Speed isn’t the only thing that determines the performance of a computer language. Another parameter, code efficiency, will be covered in my next installment.
Listing 7. Scr # 3 0 ( FAST VERSION OF BENCHHARK ) 1 ( BY SALLY FORTH ) 2 3 0 VARIABLE READING 4 5 88 CONSTANT SCREEN 6 7 : BENCHMARK-2 8 24 GR. 9 0 19 ! 10 SCREEN @ 7680 255 FILL 11 256 19 C@ * 20 C@ + 12 READING ! 13 0 GR. 14 READING @ . 15 ." JIFFIES" CR ;
A.N.A.L.O.G. ISSUE 14 / NOVEMBER 1983 / PAGE 59
My nerdy little brother says that a program written in FORTH can take up less memory than the same program written in machine code! I bet him a week’s allowance that he’s wrong. Do I win?
Nervous in Nevada
Looks like you’ll have to split the kitty, kids. It is possible for a FORTH program to take up less space than machine code—but it isn’t very likely on an ATARI computer. Let’s take a look at the factors that control the code efficiency of FORTH and M/L programs.
At the innermost core of every FORTH system is a block of machine-language routines called the kernel. The kernel can be thought of as FORTH’s operating system; it maintains the stacks, controls memory allocation and performs all the dirty little housekeeping duties that make FORTH look like FORTH. When you compile new words into a FORTH dictionary, all you are doing is defining new execution patterns for the fundamental FORTH routines inside the kernel.
Each of the FORTH systems available for the ATARI come with a “bare bones” kernel of fixed size. For example, the valFORTH 1.1 kernel takes up about 7.5K of RAM; the Team ATARI fig-FORTH kernel, about 8.9K. Because the kernel must be permanently linked with your program in order for it to run, the size of the kernel determines the absolute minimum size of your program. So even if you wrote a valFORTH program consisting of just one word:
( Change background color to black ) : PROGRAM 0 710 C! ;
the final product would still occupy at least 7.5K! Machine language laughs at the idea of a kernel. It requires no overhead at all because it communicates directly to the 6502 microprocessor at the lowest possible level. In fact, you can write a little routine in assembly like this one:
A900 LDA #0 8DC602 STA 710
that performs exactly the same function as our FORTH word PROGRAM in only five bytes.
When you compare the sparseness of machine code to FORTH’s kernel overhead, it’s hard to conceive of an application where FORTH would be more efficient. Yet even as the kernel taketh away, the kernel giveth—in the form of generality.
A FORTH kernel is like a Swiss Army knife. It consists of a number of all-purpose tools (subroutines) built around a simple, versatile control structure (the stack). A knowledgeable FORTH programmer can exploit the built-in features of the kernel to concisely implement all sorts of elaborate procedures, much as an assembly hacker uses OS subroutines as often as possible to simplify his work. But a FORTH kernel is far more versatile than a machine-specific OS; and although the initial size of a FORTH program is large, its threaded structure makes it grow less quickly than a machine-language program. Theoretically, a point can be reached where FORTH and machine code take up the same amount of RAM (see Figure 1), after which the FORTH application will require less memory than the same job written in pure machine code.
When is this break-even point reached? It depends on the nature of the application, the power of the FORTH kernel and the skill of the programmer. Generally speaking, your FORTH program has to get fairly large before it will begin to compete with the best RAM-cramming efforts of an assembly hacker. That’s why you’re not likely to realize the potential efficiency of FORTH with a computer as small as the ATARI. On larger machines, however, FORTH can make a big difference when it comes to saving memory.
One way to reduce the size of a FORTH program is to run it through a utility called a target compiler. A target compiler analyzes your application and strips away all the unnecessary gunk and dribble, leaving you with a tight package of object code that is smaller and maybe even a little faster than the original. None of the FORTH systems available for the ATARI offer a target compiler, although Valpar International is supposed to have a valFORTH compiler in the works.
As I mentioned above, the threaded architecture of FORTH makes a big contribution to its efficiency. But it takes good, structured programming techniques to realize this benefit. We’ll discuss the controversial subject of structured programing in my next installment.
Sally welcomes your questions about the FORTH programming language, and will publish the most interesting letters in future columns. Write to her c/o A.N.A.L.O.G. Computing, RO. Box 23, Worcester, MA 01603.
A.N.A.L.O.G. ISSUE 15 / JANUARY 1984 / PAGE 20
My original plan for this month was to talk about the concept of modular programming, and what it means to you, the Atari FORTH programmer. But Joel Gluck’s column Our Game (page 40) is coincidentally devoted to the same subject; and I’ve got lots of reader mail piling up in my hopper. So let’s violate the “age before beauty” maxim just this once, and allow Joel to extoll the wonders of modular programming. Rest assured that everything he says applies in principle to FORTH as well as BASIC. Where’s my letter opener?
Please review different implementations of FORTH for the Atari. This would enable new FORTH users to select their version, and would let present users know what features are available in other variations (I have PNS-FORTH).
That’s a tall order, Mr. Brazee. It turns out that there are no less than six commercial implementations of FORTH available for the Atari, plus a number of “underground” systems available through various grapevines and user groups. The cruel editors won’t give me enough space to review all of them in depth, so you’ll have to contact the publishers at the addresses provided if you need more information.
This is an unspectacular but usable version of standard 6502 fig-FORTH, modified for the Atari. It comes complete with a Ragsdale-type assembler, fig’s clumsy Editor, a handful of debugging tools and a nice little set of operating system and floating point definitions. The graphics words provided with APX FORTH give you about as much picture-drawing power as Atari BASIC. Thankfully, APX also offers a couple of extension packages (fun-FORTH by Joel Gluck for $24.95; FORTH Turtle Graphics Plus by William Volk for $17.95) which make the basic system a lot more attractive as far as graphics are concerned. Another accessory package, FDOS by George Schwenk, allows APX FORTH users to access standard Atari DOS files ($39.95 from Superware, 2028 Kingshouse Road, Silver Spring, MD 20904). APX FORTH’s 50-page reference manual assumes prior familiarity with FORTH; a copy of fig’s Handy Reference Guide with printouts of all extension screens are thoughtfully included.
QS FORTH was the first FORTH system offered commercially for the Atari. Like APX FORTH, it offers limited graphics support and very few bells and whistles. An assembler and LOCATEing screen editor are included, but there are no provisions for floating-point math and minimal debugging facilities. The documentation for QS FORTH is better than average. It’s a 160-page notebook, professionally printed and arranged like a mini-tutorial on FORTH, with many useful examples and a good description of the internal workings of the system. Too bad the only printed source listing is for the assembler.
This FORTH (marketed chiefly by Mountain View Press) is crammed full of goodies for the Atari stacker, including a sophisticated player/missile graphics system that uses vertical blank interrupts (VBIs) to simulate multi-tasking. Ragsdale’s assembler, a very usable screen editor (second only to valFORTH’s), a system glossary, complete source listings and a strange but serviceable string-handling vocabulary are also included. Surprisingly, there is no floating-point support. The photocopied reference manual is impressively thick and comprehensive but very poorly packaged; where did they find a stapler mighty enough to handle one solid inch of paper?
Unlike most Atari FORTHs, which are based on the familiar fig-FORTH subset, Mountain View Press’s official FORTH system implements the full FORTH-79 standard. It’s also the only Atari-compatible FORTH that offers any potential for transportability: the MVP dialect is available on just about every micro you can name. The basic $ 100.00 ECS/MVP system is competent and includes a reasonable screen editor, but it lacks the special audio-visual features that make Atari folks sit up and take notice. $75.00 more will get you a package of interesting graphics and animation extensions, a “multitasking” sound vocabulary and an unlimited runtime license. Neither system supports floating-point. The scant reference manual assumes that you have access to the Haydon book All About FORTH (available, like most important FORTH literature, from MVP). If Mountain View and ECS Software decide to support Atari MVP-FORTH as well as the IBM-PC and Apple II versions (with source listings, target compilers and other professional delights), MVP could become the FORTH of choice for serious Atari software authors.
Aside from the usual editor and I/O access features, this fig-based system includes limited player/missile graphics support, a couple of sound definitions and a few handy debugging words. If you need floating point math, purchase the optional Floating Point Package ($29.95). Elcomp also offers a beginner’s mini-system called Learn-FORTH on either disk or cassette; its $19.95 price tag makes it one of the least expensive introductions to FORTH programming on the Atari.
I’ve saved the best for last. valFORTH is the most complete FORTH implementation available for the Atari. It’s a language system in the truest sense of the word: everything you need to develop, debug and sell finished FORTH applications is included. Valpar’s basic $59.95 introductory package includes a choice of two screen editors (fig’s and a slightly better custom version), a souped-up 6502 assembler, a good floating-point package plus operating system I/O and memory allocation words. Documentation and packaging are superb; they even supply dividers for the notebook and little labels for the dividers!
Valpar’s extension packages really make valFORTH sing. Must-haves include the Editor /Utilities package ($49.95, containing Valpar’s magnificent 1.1 Video Editor and a nifty string-handling system) and the Player /Missile Graphics package ($49.95 ). Other interesting but less essential options include the Display Formatter (a custom display list generator, $39,95); valGraphics (high-speed turtles, $54.95); Text Compression/Formatting (an adventure-writing toolkit, $39.95); and valDOS ($59.95).
The power and accessibility of valFORTH have already established it as the de facto FORTH standard of the Atari community. Until somthing better comes along, I’ll continue to use it for all of the FORTH demos appearing in this column.
The only underground FORTH system worth mentioning goes by a number of different names. “Free FORTH,” “Sunnyvale FORTH” and “Team FORTH” all stand for the same public-domain package developed somewhere in the San Francisco Bay area by Steve Calfee, Harold Striepe, Peter Lipson and Robin Ziegler (among others). The system consists of two disks: a system disk with bare-bones kernel and several utility screens, and a tutorial disk with all the utilities in compiled form. It’s not the best documented or most advanced FORTH system in the world, but it’s dandy for experimenting and the price is hard to beat. Where to find it? Your local user group library is the likeliest place. Failing that, check your local bulletin board system or computer store for leads.
One of the main reasons I got an Atari was so that I could learn how to program games, and I don’t doubt that this is true of many ANALOG readers. Although I’ve read that FORTH is good for writing game programs, I have yet to see any code in FORTH for an “arcade type” game. So here is a question for you: Where can 1 find an example of a FORTH game program, similar to those which appear in every issue of ANALOG in BASIC and machine language? How about treating ANALOG’s FORTH audience to a complete game, instead of just a few example screens and snatches of code?
Your hopes are shared by many of my readers, Joe. I certainly have no objection to publishing a game in FORTH, and neither do the editors of ANALOG as long as the source code is of reasonable length. If any of my readers would like to share their FORTH shoot-em-ups with the rest of us, send me the source screens (on disk, please) in care of ANALOG and I’ll be happy to publish them with your name on the top.
Old Sally’s been working on a few game ideas of her own. Next month, I’ll present a super-high-speed PLOT routine that will form the basis for a very ambitious entertainment program.
Sally welcomes your comments about the FORTH programming language, and will publish her most interesting letters in future issues. Write to her c/o ANALOG Computing Magazine, P.O. Box 23, Worcester, MA 01603.
A.N.A.L.O.G. ISSUE 16 / FEBRUARY 1984 / PAGE 80
Here’s FPLOT, the ultrahigh-speed graphics plotting routine I promised to bring you last month. It’s designed exclusively for Atari’s high-resolution 2-color mode “F” (GRAPHICS mode 24 in BASIC). To use it, you must have valFORTH’s standard graphics library and assembler LOADed into your system. The definition eats up a hefty chunk of dictionary space, but who cares? FPLOT is fast.
Although the source screens are mostly self-documenting, a few clarifications are in order for the sake of beginners. FPLOT uses a technique known as “table lookup” to eliminate the time-consuming calculations used by the PLOT routine in Atari’s Central Input/Output (CIO) utility. The twin ALLOT words in screen 1 set aside 384 bytes for the lookup tables; screens 2 and 3 set up negative and positive bit-masks for the plot logic. Screens 4 through 7 define an assembly-language word (PLOTSETUP) that initializes the lookup tables. It must be executed immediately after setting up the high-res screen (with a 24 GR. instruction) or FPLOT will not work. I could have written PLOTSETUP in FORTH, but speed is the reason for this entire exercise, right?
Screens 8 through 12 contain the assembly-language definition of FPLOT. Syntax is identical to the PLOT word in valFORTH’s standard graphics package. The color to be plotted (0–1) is specified with the valFORTH word COLOR, just like the ordinary PLOT. Strange things will happen if your plotting parameters exceed 319 for the X-coordinate, or 191 for the Y. FPLOT’s lack of automatic range checking is part of the price you pay for extra speed.
You can use FPLOT with the valFORTH word DR. (DRAWTO) because it saves the X and Y coordinates in the operating system locations COLCRS and ROWCRS. If you’re not using DR. and you want to make FPLOT even faster, eliminate Line 9 in screen 9 and Lines 6, 7, and 9 in screen 10.
The demo words in screens 13–17 will show you just how much speed you can expect from FPLOT. I obtained screen-fill times of 9957 jiffies for the CIO plot and just 1632 jiffies for PLOT, a 6-times improvement. In case you’re wondering, it takes Atari BASIC 24554 jiffies to fill a mode F screen with PLOTS!
Next month, I’ll show you how to implement megaspeed line-drawing with FPLOT.
SCREEN #1 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 ( Reserve space for tables ) 3 4 DECIMAL 5 6 LABEL YLOWS 192 ALLOT ( lsbs ) 7 LABEL YHIGHS 192 ALLOT ( msbs ) 8 9 ( OS equates ) 10 11 84 CONSTANT ROWCRS 12 85 CONSTANT COLCRS 13 88 CONSTANT SAVMSC 14 15 --> SCREEN #2 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 2 BASE ! ( for convenience ) 3 4 LABEL NMASKS ( plot masks ) 5 6 11111110 C, 11111110 C, 7 11111101 C, 11111101 C, 8 11111011 C, 11111011 C, 9 11110111 C, 11110111 C, 10 11101111 C, 11101111 C, 11 11011111 C, 11011111 C, 12 10111111 C, 10111111 C, 13 01111111 C, 01111111 C, 14 15 --> SCREEN #3 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 LABEL PMASKS ( more masks ) 3 4 0 C, 1 C, 5 0 C, 10 C, 6 0 C, 100 C, 7 0 C, 1000 C, 8 0 C, 10000 C, 9 0 C, 100000 C, 10 0 C, 1000000 C, 11 0 C, 10000000 C, 12 13 DECIMAL 14 15 --> SCREEN #4 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 ASSEMBLER ( new vocabulary ) 3 4 N 4 + CONSTANT PNTR 5 N 3 + CONSTANT XHI 6 N 2 + CONSTANT XLO 7 N CONSTANT YPOS 8 9 ( PLOTSETUP initializes the 10 ( YLOWS and YHIGHS tables for 11 ( use by the high-speed plot 12 ( routine. It should be called 13 ( immediately after setting up 14 ( graphics mode 24. ) 15 --> SCREEN #5 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 CODE PLOTSETUP 3 4 XSAVE STX, ( preserve X ) 5 SAVMSC 1+ LDA, ( get msb of ) 6 YPOS STA, ( screen addr ) 7 SAVMSC LDY, ( and also lsb ) 8 # 0 LDX, ( init index ) 9 CLD, ( for safety ) 10 11 BEGIN, ( start a loop ) 12 13 YPOS LDA, ( put msb into ) 14 YHIGHS ,X STA, ( msb table ) 15 --> SCREEN #6 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 TYA, ( put lsb into ) 3 YLOWS ,X STA, ( lsb table ) 4 CLC, ( for addition ) 5 # 40 ADC, ( new offset ) 6 7 CS IF, ( if carry set, ) 8 YPOS INC, ( increment msb ) 9 ENDIF, 10 11 TAY, ( save new lsb ) 12 INX, ( update index ) 13 # 192 CPX, ( done 192 yet? ) 14 15 --> SCREEN #7 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 EQ UNTIL, ( loop until ) 3 ( index = 192 ) 4 5 XSAVE LDX, ( restore X-reg ) 6 NEXT JMP, ( back to FORTH ) 7 8 C; ( end PLOTSETUP ) 9 10 11 12 13 14 15 --> SCREEN #8 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 ( FPLOT is the actual plotting 3 ( word. Syntax is the same as 4 ( the standard CIO-type PLOT: 5 6 ( x-pos y-pos --- 7 8 ( X-pos can range fro 0-319. 9 ( Y-pos range is 0-191. No 10 ( range checks are performed 11 ( to save time, so be careful! 12 ( Plot color is controlled by 13 ( the standard valFORTH word 14 ( COLOR. Legal COLOR values 15 ( are 0 and 1. ) --> SCREEN #9 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 CODE FPLOT 3 4 # 2 LDA, ( # DROP values ) 5 SETUP JSR, ( move into N ) 6 XSAVE STX, ( preserve X ) 7 8 YPOS LDX, ( y-pos is used ) 9 ROWCRS STX, ( as an index ) 10 YLOWS ,X LDA, ( into lsb/msb ) 11 PNTR STA, ( offset tables ) 12 YHIGHS ,X LDA, ( to create a ) 13 PNTR 1+ STA, ( z-page pntr ) 14 15 --> SCREEN #10 0 ( HIGH-SPEED MODE F PLOTTER ) 1 2 ( PNTR now contains the 3 ( absolute RAM address of the 4 ( desired mode line. ) 5 6 XHI LDA, ( get msb x-pos ) 7 COLCRS 1+ STA, ( for OS usage ) 8 XLO LDA, ( get lsb x-pos ) 9 COLCRS STA, ( also for OS ) 10 XHI LSR, ( divide x-pos ) 11 .A ROR, ( by 2 ) 12 .A LSR, ( then 4 ) 13 .A LSR, ( and then 8 ) 14 TAY, ( save x/8 in Y ) 15 --> SCREEN #11 0 ( HIGH-SPEED MODE F PLOTTER ) 1 2 XLO LDA, ( get lsb again ) 3 # 7 AND, ( mask to get 4 ( bit position 5 ( in plot byte ) 6 .A ASL, ( multiply by 2 ) 7 CLRBYT ORA, ( & superimpose 8 ( bit 0 color 9 ( data for use ) 10 TAX, ( as an index 11 ( into the mask 12 ( tables ) 13 PNTR )Y LDA, ( get plot byte ) 14 NMASKS ,X AND, ( mask plot bit ) 15 --> SCREEN #12 0 ( HIGH-SPEED MODE F PLOTTER ) 1 2 PMASKS ,X ORA, ( superimpose ) 3 ( the plot bit ) 4 PNTR )Y STA, ( and show the ) 5 ( new byte ) 6 XSAVE LDX, ( restore X-reg ) 7 NEXT JMP, ( and return ) 8 9 C; ( end FPLOT ) 10 11 12 13 14 15 --> SCREEN #13 0 ( FPLOT DEMONSTRATION ) 1 2 ( PLOTEST and FPLOTEST compare 3 ( the speed of the standard 4 ( CIO-type PLOT with FPLOT. 5 6 ( They PLOT all 61,440 pixels 7 ( on a full Mode F screen, 8 ( using the system jiffy 9 ( timers to clock the speed. 10 11 ( Be sure you've LOADed the 12 ( valFORTH graphics library 13 ( into your system before 14 ( LOADing these words! ) 15 --> SCREEN #14 0 ( FPLOT DEMONSTRATION ) 1 2 19 CONSTANT TOCK ( the timer ) 3 20 CONSTANT TICK ( locations ) 4 5 0 VARIABLE TIMING 6 7 ( Set up screen & colors ) 8 9 : GREADY 10 24 GR. 11 1 0 14 SE. ( white pixels ) 12 2 0 0 SE. ( black bkgnd ) 13 1 COLOR ; 14 15 --> SCREEN #15 0 ( FPLOT DEMONSTRATION ) 1 2 ( Calculate & display 3 ( timer reading ) 4 5 : SHOWTIME 6 TOCK C@ 256 * 7 TICK C@ + 8 TIMING ! 9 0 GR. 10 TIMING @ . 11 ." jiffies with " ; 12 13 14 15 --> SCREEN #16 0 ( FPLOT DEMONSTRATION ) 1 2 ( Fill screen with CIO PLOTs ) 3 4 : PLOTEST 5 GREADY 6 0 TOCK ! ( zero timers ) 7 192 0 DO 8 320 0 DO 9 I J PLOT 10 LOOP 11 LOOP 12 SHOWTIME 13 ." CIO PLOT." CR ; 14 15 --> SCREEN #17 0 ( FPLOT DEMONSTRATION ) 1 2 ( Fill screen with FPLOTs ) 3 4 : FPLOTEST 5 GREADY 6 PLOTSETUP ( init plot tables ) 7 0 TOCK ! ( zero timers ) 8 192 0 DO 9 320 0 DO 10 I J FPLOT 11 LOOP 12 LOOP 13 SHOWTIME 14 ." FPLOT." CR ; 15
A.N.A.L.O.G. ISSUE 17 / MARCH 1984 / PAGE 46
I want to thank you for your article in Issue #13 of ANALOG. It was a great help in learning FORTH. I was able to compare the BASIC and FORTH programs, and finally understood a couple of simple techniques.
I’ve been playing with valFORTH for almost six months with very little success. True, I haven’t devoted all of my energies to learning FORTH, but I do have about ten different books on the language. They all fail in one thing: How to actually program something. They go to great pains to explain the stack, numbers, and why the language is great. But examples of code and explanations of the how and whys, they do not have.
I have a question that you might be able to address in one of your articles. I read keycodes a lot in my BASIC programs, but I have yet to figure out what I have to do to perform the same function in FORTH. My BASIC code looks something like this:
10 KEYBOARD=53775:UNTOUCH=255:RESPONSE=764 300 KEY=PEEK(KEYBOARD):IF KEY=UNTOUCH THEN 300 310 IF PEEK(RESPONSE)=15 THEN CN=CN+1:GOTO 490 320 IF PEEK(RESPONSE)=14 THEN CN=CN-1:GOTO 490 330 IF PEEK(RESPONSE)=12 THEN 520 340 GOTO 300
My main hangup is figuring out which control structure to use (IF/ELSE, BEGIN/UNTIL or WHILE/REPEAT) to accomplish the job of Line 300. When I use the debugger and watch the stack, the values always seem to be wrong.
I appreciate any help you would be willing to provide. Please keep the articles coming. There’s a lot of interest in my local user group in using FORTH, but it seems to be too difficult to learn.
Corpus Christi, Texas
Don’t give up hope! Your problem is a bad case of BASIC On The Brain, not incurable, but difficult to shake off.
Your BASIC code for reading the keyboard is more complicated than it has to be. You could do the same thing with:
10 KEYCODE=764:CLEAR=255 300 K=PEEK(KEYCODE):IF K=CLEAR THEN 300 310 POKE KEYCODE,CLEAR:IF K=15 THEN CN=CN+1:GOTO 490 320 IF K=14 THEN CN=CN-1:GOTO 490 330 IF K=12 THEN 520 340 GOTO 300
The FORTH equivalent looks like this:
( First, declare the variables ) ( K and CN. ) 0 VARIABLE K ( holds last keypress ) 0 VARIABLE CN ( program variable ) ( Now declare KEYCODE and RESET as ) ( FORTH constants. ) 764 CONSTANT KEYCODE 255 CONSTANT RESET ( The following definition is used to ( scan the hardware's keyboard ( register until a key is pressed. ( The register is then reset and the ( internal keycode is left on the ( stack. ) : GETKEY ( --- n ) BEGIN KEYCODE CG RESET <> UNTIL KEYCODE C@ RESET KEYCODE C! ; ( Let's define a couple of dummy ( words [LINE490 and LINE520] ( to represent the corresponding ( lines of BASIC code. You would ( replace these with definitions that ( suit your application. ) : LINE490 ." LINE 490 " ( display line # ) ." CN = " ( and value of ) CN @ . CR ; ( variable CN ) LINE528 ." LINE 520 " ( same as LINE490 ) ." CN = " CN @ . CR ; : DOKEY ( the control selector ) BEGIN ( start indefinite loop ) GETKEY ( fetch keypress and ) K ! ( save it in K ) K @ 15 ( IF K=15 ) IF 1 CN +! ( CN=CN+1 ) LINE490 ( execute LINE490 ) 1 ( leave a "true" flag ) ( on stack to exit ) ( BEGIN/UNTIL loop ) ELSE ( Otherwise ... ) K @ 14 = ( IF K=14 ) IF -1 CN +! ( CN=CN-1 ) LINE490 ( do LINE490 ) 1 ( and exit loop ) ELSE K @ 12 = ( IF K=12 ) IF LINE520 ( do LINE520 ) 1 ( and exit ) ELSE ( if all else ) ( fails, ) 0 ( tell UNTIL to ) ( keep looping ) ENDIF ENDIF ENDIF UNTIL ( end indefinite loop ) ; ( end definition )
I assume that your prodigious FORTH library includes Leo Brodie’s Starting FORTH (Prentice-Hall). Concentrate your reading on this book; it’s the clearest introduction to the FORTH language ever published.
I goofed. Last month’s FPLOT utility had a couple of itsy-bitsy bugs, which I didn’t notice until it was too late to fix them in the magazine.
The ROWCRS and COLORS constants declared in screen #1 refer to the wrong locations. Even more embarrassing, my plot-mask tables PMASKS and NMASKS were written backwards! FPLOT still works after a fashion, but the horizontal positioning of the pixels is somewhat skewed; and you can’t follow up an FPLOT with an OS DRAWTO with reliable results. Blame it on a Christmas bottle of Bailey’s Irish Cream, which makes even reverse Polish notation look sensible in holiday doses.
The remedy is easy and effective. Simply replace screens 1-3 of the original FPLOT listing with the following:
SCREEN #1 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 ( Reserve space for tables ) 3 4 DECIMAL 5 6 LABEL YLOWS 192 ALLOT ( lsbs ) 7 LABEL YHIGHS 192 ALLOT ( msbs ) 8 9 ( OS equates ) 10 11 90 CONSTANT ROWCRS ( REVISED ) 12 91 CONSTANT COLCRS ( REVISED ) 13 88 CONSTANT SAVMSC 14 15 --> SCREEN #2 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 2 BASE ! ( for convenience ) 3 4 LABEL NMASKS ( plot masks ) 5 6 01111111 C, 01111111 C, 7 10111111 C, 10111111 C, 8 11011111 C, 11011111 C, 9 11101111 C, 11101111 C, 10 11110111 C, 11110111 C, 11 11111011 C, 11111011 C, 12 11111101 C, 11111101 C, 13 11111110 C, 11111110 C, z4 15 --> SCREEN #3 0 ( HIGH SPEED MODE F PLOTTER ) 1 2 LABEL PMASKS ( REVISED ) 3 4 0 C, 10000000 C, 5 0 C, 1000000 C, 6 0 C, 100000 C, 7 0 C, 10000 C, 8 0 C, 1000 C, 9 0 C, 100 C, 10 0 C, 10 C, 11 0 C, 1 C, 12 13 DECIMAL 14 15 -->
Now that FPLOT is fully operational, we can move on to more advanced graphics concepts like line drawing. Screens 18-31 contain the valFORTH assembly definition of VFAST, which draws vertical lines in ANTIC mode F at breathtaking speed. It’s great for filling in blocks of screen, drawing bar charts and various types of animation. And it works. I promise!
VFAST and the demos that follow it share many of the FORTH words we defined last month for FPLOT. That means you have to compile the FPLOT screens into your system first, before loading VFAST. Here’s the procedure for LOADing and testing VFAST:
The rules for using VFAST are similar to those for FPLOT. First, set up the high-res screen with a 24 GR. call. Next, execute the word PLOTSETUP to initialize the look-up tables that make FPLOT and VFAST so swift. Don’t forget to call PLOTSETUP or your machine will lock up the instant you call VFAST! Select a drawing color with n COLOR, where n equals 1 for foreground or for background. Both FPLOT and VFAST are now ready to use. The syntax of VFAST is:
X1 Y1 Y2 ---
where XI is the X-position of the desired vertical line, Y1 is the Y-position of the first endpoint you want to draw and Y2 the position of the other endpoint. Thus the command:
20 20 50 VFAST
will produce exactly the same result onscreen as:
20 20 PLOT ( or FPLOT ) 20 50 DR.
except that VFAST will finish drawing a lot sooner.
VFAST is friendlier than FPLOT because it checks to make sure your parameters are within legal ranges. Pass VFAST a bad X1, Y1 or Y2 value and it will jump back into FORTH without drawing anything. Venturesome programmers may wish to modify VFAST so that it “clips” lines at the edge of the screen. Incidentally, VFAST leaves the coordinates of the last point it has drawn (XI, Y2) in the Atari’s OLDROW and OLDCOL registers, so you can follow it up with a regular DRAWTO command if you like and the operating system will never know the difference. I know I said that last month too, but this time I really mean it!
That’s all there is to lightning-fast vertical lines in valFORTH. Bet you can’t guess what I’ll be showing you next month.
SCREEN #18 0 ( VFAST Vertical Line Drawing ) 1 2 ( REMEMBER: You MUST have the 3 ( FORTH screens from Issue #16 4 ( [pp. 81-83] compiled into 5 ( your system BEFORE you LOAD 6 ( these screens! And don't 7 ( forget to repair screens 1-3 8 ( as shown in this month's 9 ( column, either. ) 10 11 ASSEMBLER 12 112 CONSTANT MASKB 13 113 CONSTANT LEFT 14 114 CONSTANT VECT 15 115 CONSTANT SADDR --> SCREEN #19 0 ( VFAST Vertical Line Drawing ) 1 2 ( This is the assembly code for 3 ( VFAST. Syntax is: 4 5 ( X1 Y1 Y2 --- 6 7 ( WHERE: 8 ( X1 is the fixed horizontal 9 ( position of the line [0-319], 10 ( Y1 is the Y-coordinate of 11 ( the first end of the line you 12 ( want to draw [0-191]. and 13 ( Y2 is the Y-coordinate of the 14 other end [0-191]. ) 15 --> SCREEN #20 0 ( VFAST Vertical Line Drawing ) 1 2 ( Line color is controlled by 3 ( the standard ual FORTH word 4 ( COLOR. Legal COLOR values 5 ( are 8 and I. See valFORTH's 6 ( documentation for more info. ) 7 8 CODE VFAST 9 10 # 3 LDA, ( # DROP values ) 11 SETUP JSR, ( move into N ) 12 XSAVE STX, ( preserve X ) 13 CLD, ( for safety ) 14 15 --> SCREEN #21 0 ( VFAST Vertical Line Drawing ) 1 2 3 PNTR LDA, ( get lsb of X1 ) 4 PNTR 1+ LDY, ( and msb ) 5 # 1 CPY, ( if X1 is ) 6 ( greater than ) 7 CS IF, 8 # 64 CMP, ( 320, abort ) 9 ( the VFAST and ) 10 CS IF, ( return to ) 11 NEXT JMP, ( FORTH ) 12 ENDIF, 13 14 ENDIF, 15 --> SCREEN #22 0 ( VFAST Vertical Line Drawing ) 1 2 COLORS STA, ( save X1 for ) 3 COLORS 1+ STY, ( the OpSys ) 4 5 XLO LDA, ( if Y1 is ) 6 # 192 CMP, ( greater than ) 7 ( 192, abort ) 8 CS IF, 9 NEXT JMP, ( the VFAST ) 10 ENDIF, 11 12 N LDA, ( also be sure ) 13 # 192 CMP, ( Y2 is within ) 14 15 --> SCREEN #24 0 ( VFAST Vertical Line Drawing ) 1 2 ELSE, ( if Y2 is ) 3 ( larger, then ) 4 XLO LDA, ( calc the ) 5 SEC, ( difference & ) 6 N SBC, ( set drawing ) 7 # 255 LDY, ( direction to ) 8 ( "up" [-1] ) 9 ENDIF, 10 11 ( save the ) 12 LEFT STA, ( delta and the ) 13 VECT STY, ( direction for ) 14 ( later use ) 15 --> SCREEN #25 0 ( VFAST Vertical Line Drawing ) 1 2 PNTR LDA, ( fetch X1 and ) 3 PNTR 1+ LSR, ( divide it by ) 4 .A ROR, ( 8 for use as ) 5 .A LSR, ( a Y-index ) 6 .A LSR, ( into the ) 7 TAY, ( mode line ) 8 9 PNTR LDA, ( now mask X1 ) 10 # 7 AND, ( to get bit ) 11 .A ASL, ( position, ) 12 CLRBYT ORA, ( superimpose ) 13 ( COLOR data & ) 14 MASKB STA, ( save result ) 15 --> SCREEN #26 0 ( VFAST Vertical Line Drawing ) 1 2 XLO LDX, ( init Y-coord 3 ROWCRS STX, ( for drawing 4 5 ( Here begins the loop that 6 ( actually draws the line for 7 ( you. ) 8 9 BEGIN, 10 11 YLOWS ,X LDA, ( fetch addr of ) 12 SADDR STA, ( first byte in ) 13 YHIGHS ,X LDA, ( the mode line ) 14 SADDR 1+ STA, 15 --> SCREEN #27 0 ( VFAST Vertical Line Drawing ) 1 2 3 MASKS LDX, ( retrieve the ) 4 ( color mask ) 5 SADDR )Y LDA, ( fetch the ) 6 ( proper screen ) 7 ( byte. ) 8 NMASKS ,X AND, ( zero out the ) 9 ( plot bit. ) 10 PMASKS ,X ORA, ( super impose ) 11 ( color data & ) 12 SADDR )Y STA, ( proudly show ) 13 ( the byte ) 14 15 --> SCREEN #28 0 ( VFAST Vertical Line Drawing ) 1 2 ROWCRS LDA, ( update the ) 3 CLC, ( Y-coord for ) 4 VECT ADC, ( the next plot ) 5 ROWCRS STA, ( restore table ) 6 TAX, ( index ) 7 LEFT DEC, ( keep drawing ) 8 ( till no more ) 9 EQ UNTIL, ( Y-coords left ) 10 11 XSAVE LDX, ( restore X and ) 12 NEXT JMP, ( we're done! ) 13 C; --> 14 15 SCREEN #29 0 ( VFAST Demonstration Words ) 1 2 ( You remembered to compile 3 ( last month's screens, didn't 4 ( you? Of course you did. ) 5 6 : LINEINIT 7 8 24 GR. ( graphics mode ) 9 14 709 ! ( init screen & ) 10 1 COLOR ( line colors ) 11 0 TOCK ! ( reset timers ) 12 13 14 15 --> SCREEN #30 0 ( VFAST Demonstration Words ) 1 2 : SLOWLINE ( using Atari's ) 3 ( CIO PLOT and DRAW ) 4 LINEINIT 5 6 320 0 DO 7 I 0 PLOT I 191 DR. 8 LOOP 9 10 SHOWTIME ." CIO lines." 11 12 CR ; 13 14 15 --> SCREEN #31 0 ( VFAST Demonstration Words ) 1 2 : FASTLINE ( using our new ) 3 ( word VFAST ) 4 LINEINIT 5 PLOTSETUP ( don't forget! ) 6 7 320 0 DO 8 I 0 191 VFAST ( simple, ) 9 LOOP ( eh? ) 10 11 SHOWTIME ." VFAST." 12 13 CR ; 14 15
A.N.A.L.O.G. ISSUE 18 / MAY 1984 / PAGE 38
The mUse article on page 112 of Issue # 14 states that BASIC arrays eat up RAM. Each array element requires six bytes, meaning a 10 × 10 array uses 600 bytes. Is FORTH as wasteful?
If FORTH uses six bytes for each array element, can I simulate arrays by using strings? The mUse article says that BASIC strings are more RAM-efficient, using just “one byte per DIM allotment.”
All numbers in Atari BASIC are stored in a 6-byte floating-point format. The reason for using this format is that it allows BASIC to express very large and small numbers easily. The disadvantages of floating point are that it takes a lot of memory space to store each number, and a lot of processing time to handle them.
FORTH is considerably more flexible than Atari BASIC when it comes to storing numbers. With FORTH, you can define your own storage formats that are as efficient or wasteful as you like, limited only by the capacity of the machine.
The “default” format for a FORTH number is a 2-byte signed integer. This format allows you to express any whole number between -32768 and +32767. No fractions or decimal points are allowed; that is the price you pay for using only one-third of the storage space required by Atari BASIC.
How do you set up an array in FORTH? The method you use depends on what you’re going to do with the array. Let’s assume that you need an array called BOX with 10 elements in it. In Atari BASIC, you would type:
Notice that BOX is DIMensioned to 9, not 10. The reason is that BASIC numbers array elements starting with 0. If you DIMed BOX to 10 instead of 9, you’d actually be reserving space for 11 elements instead of 10. BASIC is wasteful enough without having useless array elements hogging up RAM!
The BOX array could be defined in FORTH like this:
VARIABLE BOX 10 ALLOT
Not too hard to grasp, except for that mysterious 18 ALLOT. What has the number 18 got to do with a 10-element array? It works like this: the FORTH word VARIABLE automatically reserves 2 bytes in the dictionary under the name BOX, enough for one signed integer. The 18 ALLOT phrase tells the compiler to reserve 18 additional bytes under the name BOX. Those 18 bytes are enough to hold 9 more 2-byte numbers. That gives BOX a total storage capacity of 20 bytes, or 10 2-byte numbers. See?
Now let’s fill our BOX arrays with numbers. We’ll assign each number a value equal to its position in the array, so that BOX (0)=0, BOX (1)=1, BOX (2)=2, etc. One way to accomplish this in BASIC is:
10 DIM BOX(9) 20 FOR I=0 TO 9 30 BOX(I)=1 40 NEXT I
In FORTH, you could try:
VARIABLE BOX 10 ALLOT 10 0 DO I I 2* BOX + ! LOOP
The above FORTH example works okay, but it isn’t as efficient as it could be. Because all of the numbers in the array are less than 255, we could use a single byte to store each one instead of two. And since the numbers are predefined, why not stuff them into the array at the same time the array is defined?
Here’s an example that will show you what I mean.
LABEL BOX 0 C, 1 C, 2 C, 3 C, 4 C, 5 C, 6 C, 7 C, 8 C, 9 C,
The word LABEL simply marks a place in the dictionary and gives it the name BOX. The following sequence of C, words simply compiles the byte values 0-9 directly into the dictionary. You end up with a “string” of 10 bytes numbered 0-9, already initialized and ready to go without any external loops! How’s that for efficiency?
I’ll leave you with a semi-useful example of how to set up and access arrays in FORTH. Let’s set up two arrays, XPOINTS and YPOINTS, each of which containing the fixed X and Y coordinates of ten points to be plotted sequentially. I’ll show you the code that will set up the arrays and plot the points, in both BASIC and in FORTH.
First, the BASIC code:
10 DIM XPOINTS(9),YPOINTS(9) 20 FOR I=0 TO 3 30 READ X:XPOINTS(I)=X:NEXT I 48 FOR I=0 TO 9 58 READ Y:YPOINTS(I)=Y:NEXT I 60 GRAPHICS 3:COLOR 1 70 FOR I=0 TO 9 80 PLOT XPOINTS(I),YPOINTS(I) 90 NEXT I 100 REM * X-COORDINATE DATA 110 DATA 20,16,20,24,12,28,16,20,24,20 120 REM * Y-COORDINATE DATA 130 DATA 6,8,8,8,9,9,10,10,10,12
Here’s the same thing in FORTH:
( First set up the x and y ( coordinate tables ) LABEL XPOINTS 20 C, 16 C, 20 C, 24 C, 12 C, 28 C, 16 C, 20 C, 24 C, 20 C, LABEL YPOINTS 6 C, 8 C, 8 C, 8 C, 9 C, 9 C, 10 C, 10 C, 10 C, 12 C, ( The following word will read the ( coordinates out of the tables and ( PLOT them ) : PLOTEM 3 GRAPHICS 1 COLOR 10 0 DO I XPOINTS + @ ( fetch x-coord ) I YPOINTS + @ ( and y-coord ) PLOT ( and PLOT then ) LOOP ( simple, eh? )
Remember: whenever possible, initialize an array by compiling the values directly into the dictionary. And use bytes instead of words when your data values are less than 255 . You’ll save all sorts of RAM space, which can then be used to accomplish more interesting things.
A.N.A.L.O.G. ISSUE 23 / OCTOBER 1984 / PAGE 18
We would like to welcome Donald Forbes to the pages of ANALOG Computing. His new column, Ask Mr. Forth, will be a regular feature in our magazine.—Ed.
The best way to learn Forth, I’ve discovered, is to teach it. You don’t know Forth? I assume, if you are reading this, that you own a Forth disk and a copy of Leo Brodie’s tutorial book Starting Forth. That makes you special. There are more than 234,000,000 people in the United States and 60,000 copies of Starting Forth in print, so that 233,940,000 Americans don’t own a copy. You can certainly tell them something.
“He who teaches learns twice” goes the old saying. “Expanding the radius of my knowledge,” Einstein remarked, “extends the periphery of my ignorance.” The French mathematician, Blaise Pascal, before him observed that, as the radius of his knowledge grew, the sphere of his ignorance increased. So…teaching, faster than anything else, will make you aware of all the gaps in your knowledge.
To teach Forth you need, first of all, a demo. Like the job-hunter who needs a resume, a portfolio (samples of previous work) and interview savvy, the Forth teacher needs a good demonstration disk that will show off the features of Forth to good advantage.
Once you have a good demo, you will want to show it off. Atari is still tops in good computer graphics, as a visit to any of the national computer shows will prove. Game designer Chris Crawford, in his trademark collarless blue shirt, told 200 members of our user group this Memorial Day weekend that “you must remember, the Atari 800 is still the top machine in its class,” and they responded to his talk with a standing ovation.
You can show the demo to your friends. Your local user group may provide an attentive audience among those who have heard of Forth but were never shown how it worked. A good demo is like money in the bank—it is good to know that it is there, and you will certainly be glad to have it at hand when the occasion arises.
The demo can be a collection of anything that will best display Forth’s many advantages. Moire patterns, Fibonnacci series, prime number benchmarks, Brodie’s letter F and Forth translations of common BASIC programs are all worthy candidates.
First, we need some good display screens. But which Forth? Forth is designed to be the universal operating system and language, created by programmers for programmers and transportable across all the micros.
There are at least six commercial implementations available for the Atari, of which James Albanese’s 1980 QS Forth from Quality Software was the first. The most complete is valFORTH, which has established itself as the de facto standard for the Atari community. My favorite for tutorials to a wide audience is Team Atari Forth, the public domain, free Forth developed in the San Francisco Bay area by Steve Calfee, Harald Striepe, Peter Lipson, Robin Ziegler and others.
Our demo should work with all of them. This presents no difficulty, if we put the text on screen letter by letter. In BASIC, we could display an A with:
10 OPEN #1,8,0,"E:" 20 GRAPHICS 2 30 PUT #1,65:REM ATASCII A
In our Forth versions, we will need the ATASCII equivalent of each alphabetic character. This code will put the numbers on the screen:
: BLANK 32 EMIT ; : ATASCII 91 65 DO I EMIT BLANK I . BLANK LOOP ;
QS Forth (after I LOAD LOAD-ED LOAD-IO) will put an A on the screen with 2 GR. 65 6 PUT. Rather than type 6 PUT after each letter, we can define a single non-Forth character (%) to do it for us (after EDITOR 1 CLEAR I LIST I L) with : % 6 PUT ; .
Calfee’s Forth requires HEX 30 LOAD DECIMAL to load the utilities and (if 34 is an empty screen) 34 WIPE 34 LIST 34 UE for our edited text. The screen must be opened for output with 83 PAD C! (where 83 is an ATASCII S) and PAD 8 0 3 OPEN. With 2 GR. 65 PUT, we can now place an A at the top of the screen. We can use : % PUT ; to avoid repeated PUT’s.
The valFORTH 1.1 disk should be loaded with the printer, assembler, color, graphics, editor and operating system words, requiring 38LOAD 76 LOAD 100 LOAD 104LOAD 140LOAD 162LOAD. Our valFORTH also requires an initialization with ATASCII S PAD C! and PAD80 3OPEN. Then 2 GR. 65 3 PUT DROP will place an A at the top of the screen. We can abbreviate this with : % 3 PUT DROP ; .
What do we use for the demo text? Remember that we have twenty columns and ten lines in the 2 OR. mode (or twenty lines if you prefer J OR.) A pad of square ruled graph paper may come in handy. Here is one choice out of many:
: DEMOTEXT 7 0 POS. 71 % 76 % 79 % 66 % 65 % 76 % 3 2 POS. 84 % 72 % 69 % 82 % 77 % 79 % 78 % 86 % 67 % 76 % 69 % 65 % 82 % 8 4 POS. 87 % 65 % 82 % 9 6 POS. 66 % 89 3 8 POS. 68 % 79 % 78 % 65 % 76 % 68 % 32 % 70 % 73 % 82 % 66 % 69 % 83 ;
If this seems tedious, remember that it gets the job done, that it will work with the Forth that you have. and it’s easy to modify. Furthermore, we now know how to translate a BASIC PUT statement into a Forth PUT statement.
What we have accomplished to date represents a significant first step: we can now place any text on the screen in graphics mode one or two. To add some excitement, let us begin by cycling all the colors through the border and background color registers. This code will work:
: DELAY 2000 0 DO LOOP ; : COLORS 255 0 DO I 712 C! I 710 C! DELAY LOOP ;
Now we can put it all together:
Scr #1 0 ( demo 1 sf 840531 ) 1 DECIMAL 2 ( QS Forth code } 3 : INITIALIZE ; 4 : % 6 PUT ; 5 ( Team Atari Forth code ) 6 : INITIALIZE 83 PAD C! 7 PAD 8 0 3 OPEN ; 8 : % PUT ; 9 ( valFORTH code ) 10 : INITIALIZE ASCII S PAD C! ; 11 PAD 8 0 3 OPEN ; 12 : % 3 PUT DROP ; 13 14 : DELAY 2000 DO LOOP ; 15 Scr #2 0 ( demo 2 sf 840531 ) 1 : DEMOTEXT 7 0 POS. 2 71 % 76 % 79 % 66 % 65 % 76 % 3 3 2 POS. 84 % 72 % 69 % 82 % 4 77 % 79 % 78 % 86 % 67 % 76 % 5 69 % 65 % 82 % 6 8 4 POS. 87 % 65 % 82 % 7 9 6 POS. 66 % 89 % 8 3 8 POS. 68 % 79 % 78 % 65 % 9 76 % 68 % 32 % 70 % 79 % 82 % 10 66 % 69 % 83 % 11 : COLORS 255 0 DO I 712 C! 12 I 710 C! DELAY LOOP ; 13 : DEMO INITIALIZE 2 GR. 14 DEMOTEXT COLORS 8 GR. ; 15
In BASIC, we could have created our text screen with:
10 GRAPHICS 2 20 ? #6;" GLOBAL" 30 ? #6;" THERMONUCLEAR" 40 ? #6;" WAR" 50 ? #6;" BY" 69 ? #6;" DONALD FORBES" 80 GOTO 80
The translation into Forth would have been easy with either valFORTH or QS Forth, because they both have a special word (G" and GR.", respectively) that is missing in Team Atari Forth. This way is less instructive, but much easier and faster:
: DEMOTWO 2 GR. 7 0 POS. G" GLOBAL" 3 2 POS. G" THERMONUCLEAR" 8 4 POS. G" WAR" 9 6 POS. G" BY" 3 8 POS. G" DONALD FORBES" ;
However, it is useful to be aware of both methods.
If we write:
: FOREVER BEGIN DEMO 0 UNTIL ;
the demo will run forever. Our demo is now off to a good start. But what do we put in it? Sound, perhaps? You undoubtedly have some ideas of your own. Certainly it should be something to show off the many unique benefits of Forth. Well, that belongs with next month’s story.
As the Marquise du Deffand wrote on July 7, 1763 to the famous illegitimate mathematician, Jean le Rond d’Alembert: “The distance doesn’t matter; it is only the first step that is difficult.”
A.N.A.L.O.G. ISSUE 24 / DECEMBER 1984 / PAGE 20
If you wish to demonstrate FORTH, there is no better way than to follow the steps of the masters. Even pygmies see farther than giants, if they stand on their shoulders.
The first question of any newcomer to FORTH is: what does it look like? We can do no better than to take the example of best-selling author Leo Brodie and of fig-founder Kim Harris in the early pages of Starting Forth. They show how a small letter F on the keyboard can become a large letter F on the screen. Here is their code, just as they present it.
SCR # 1 0 ( brodie large letter F ) 1 : STAR 42 EMIT ; 2 : STARS 0 DO STAR LOOP ; 3 : MARGIN CR IS SPACES ; 4 : BLIP MARGIN STAR ; 5 : BAR MARGIN 5 STARS ; 6 : F BAR BLIP BAR BLIP BLIP CR
In your demo, you must point out that, in FORTH, you create a new definition with a colon followed by a name (in this case, STAR) and then spell out what the definition does. Here it takes 42, which happens to be the ASCII number for an asterisk (45 would be a $), followed by EMIT, which puts the asterisk on-screen. The next definition, STARS, puts STAR inside a DO…LOOP to draw multiple stars. MARGIN is used to do a carriage return followed by a line feed and fifteen spaces. BAR and BLIP also draw on the previous definitions. In the final line, F puts them all together, so that an F on the keyboard puts a large F on the screen.
You can do this demo from the keyboard, but it makes more sense to put your code on disk as well. Then you can do your first demo from the keyboard but repeat it at will by loading it from the disk.
The six-line demo is an excellent illustration of the compactness of FORTH, but is apt to be confusing to a newcomer. A much smarter idea is to put the demo on one or two screens, with only one word or instruction per line—and copious comments after each word. This method uses much more space but makes everything many times more clear. Your audience will now be able to check that they understand the purpose of each word in the final program.
One of the pleasures of FORTH is that it is nice and compact, so that you write tight code with little typing effort. You should resist the temptation to do this at the outset. If anyone else is going to be reading your code, think about putting only one word (or a few) per line and then filling up the line with lots of comments. This takes more typing, but your audience will thank you. Furthermore, when you go back to review your old code, you won’t have to rack your brains to remember what you did. Most importantly, when you start to present the advanced features of FORTH to beginners, you will need to make sure that they follow you every step of the way, or you’ll lose them in a hurry.
Here is the one-word-per-line version of Brodie’s program.
SCR # 2 0 ( brodie large letter F ) 1 : STAR place a star on screen 2 42 save an ASCII asterisk 3 EMIT ; put asterisk on screen 4 : STARS display multiple stars 5 0 starting index of loop 6 DO begin looping N times 7 STAR display an asterisk 8 LOOP ; branch back to DO 9 : MARGIN Make 15 space margin 10 CR car. return, line feed 11 15 save number 15 12 SPACES ; display these spaces 13 : BLIP single asterisk 14 MARGIN 15-space Margin 15 STAR ; asterisk 16 : BAR display 5 asterisks 17 MARGIN 15-space Margin 18 5 save number of stars 19 STARS ; display asterisks 20 21 : F show large F 22 23 BAR five stars 24 BLIP one star 25 BAR five stars 26 BLIP one star 27 BLIP one star 28 CR ; carriage return and 29 line feed 30 31 ( Type F to show large F )
If you walk your users through the program line by line, they should have a good basic understanding of what a FORTH program looks like.
This program only puts one letter on the screen. How about putting five letters on the screen? That will certainly make a more impressive demo. We can put the word FORTH on the screen by merely repeating and adapting the code which we used for the letter F.
I made some changes to get the code to fit one or two screens. Everything is standard fig-FORTH. Note that it will run just as well on either QS Forth by James Albanese from Quality Software, valFORTH from Valpar International or Team Atari Forth by Steve Calfee and others. No matter which FORTH you are using, I urge you to get a copy of Calfee’s public domain FORTH—which may be available on a bulletin board from the Bay Area Atari User Group in San Jose, CA. There is nothing to compare with it for instructional purposes—it follows religiously the fig-FORTH model and has a valuable decompiler and disassembler which I have not seen elsewhere. Sending a $15 check to the Jersey Atari Computer Group, attention of Don Ursem, 37 Clover Lane, Randolph, NJ 07869 will get you both disks number 19 and 20.
SCR # 3 0 ( forth demo 7/88 ) 1 DECIMAL 125 EMIT : FF 70 EMIT ; 2 : FSTARS 0 DO FF LOOP ; 3 : FMAR CR 1 SPACES ; 4 : FBLIP FMAR FF ; 5 : FBAR FMAR 5 FSTARS ; 6 : F FBAR FBLIP FBAR FBLIP ; 7 : OO 79 EMIT ; : SP SPACES ; 8 : OSTARS 0 DO OO LOOP ; 9 : OMAR CR 8 SP ; 10 : OBAR OMAR OO SP OO ; 11 : OBLIP OMAR OO SPACE 3 OSTARS 12 : O OBLIP OBAR OBAR OBLIP ; 13 : RR 82 EMIT ; 14 : RSTARS 0 DO RR LOOP ; 15 : RMAR CR 15 SP ; 16 : RBAR RMAR 4 RSTARS ; 17 : RBLIP RMAR RR 3 SP RR ; 18 : R RBAR RBLIP RBAR RBLIP ; 19 : TT 84 EMIT ; 20 : TSTARS 0 DO TT LOOP ; 21 : TMAR CR 21 SP ; 22 : TBAR TMAR 5 TSTARS ; 23 : TBLIP TMAR 2 SP TT ; 24 : T TBAR TBLIP TBLIP TBLIP ; 25 : HH 72 EMIT ; 26 : HSTARS DO HH LOOP ; 27 : HMAR CR 28 SP ; 28 : HBLIP HMAR HH 3 SP HH ; 29 : HBAR HMAR 5 HSTARS ; 30 : H HBLIP HBLIP HBAR HBLIP ; 31 : FOR 125 EMIT F O R T H QUIT ;
The first line has DECIMAL followed by 125 EMIT. The word DECIMAL was included to make sure that we did not default to HEX, as Calfee’s disk will do. The statement 125 EMIT clears the screen, since 125 is the ATASCII clear screen character. SP is just my abbreviation here for SPACES. Instead of STAR, I used FF for F (which is 70 EMIT), OO for 0 (which is 79 EMIT), and so on. MARGIN, for example, was abbreviated to FMAR and TMAR to squeeze each instruction into one line. As the last line shows, typing FOR will put up the word FORTH, so as to fill the whole screen. This makes for a much nicer demo.
Now is a good time to bring up the question: what is FORTH? Whether you want to pose this question to your audience is up to you. If they are just looking for entertainment, you may want to postpone it. But, if they have any background in computer hardware, assembly language or mathematics, they may find it fascinating. And, once anyone is hooked on FORTH, there is no turning back.
We can always explain, as most people want to do, that FORTH is a computer language and operating system, but this seems like trying to describe an automobile as a “horseless carriage” to an Eskimo who has never seen a horse. He will keep wondering about the strange animal called a horse and never give a thought to the carriage. When you ask him to repeat the explanation, he won’t know where to begin. What does FORTH do? A good question. Before answering, how about another question: what does an automobile do?
“The way to describe an automobile is first by thinking about what it is for, about its function, and note the list of items that make up its structure,” according to Harry Katzan in his book on systems design and documentation. “If you think about its function (what it is for), you won’t describe it by talking about its four wheels, its engine, size, and so on. You will think about it as a means of transporting a few people from one place to another at a certain cost.”
We can take the same approach. In any computer system you have a user who is looking for answers, a piece of hardware (in our case, a keyboard, screen, CPU, memory, disk and printer) and a set of software which, in the final analysis, invokes some mathematical operation or algorithm. The mathematical operation may be arithmetic (+, −, *, /, ∧) or logical (AND, OR, NOT) or comparison (=, <, >). The mathematical operation hands its answer back to the software, which passes it back on to the hardware, which hands it to the user via the screen, the printer or perhaps even the disk.
Where does FORTH fit in this pattern? The central and inescapable fact is that FORTH is the complete software interface. This one reality sets FORTH apart from all other software. There is no software in your Atari until you’ve loaded the FORTH disk—FORTH is the genie that brings your hardware to life and prepares it to do your bidding.
This is really how FORTH got its start. Bill Ragsdale (an electronics engineer from California, who also belongs to the Society of American Magicians) founded the Forth Interest Group and served as its president for five years, until last April. He remarks: “FORTH then (1978) was really an operating system for what I call a crippled computer. FORTH has been treated in a receptive way by users of computers with very limited resources in terms of memory, mass storage or input/output. The Varian 620i was a crippled computer. Some of the early Hewlett-Packards, the 2100 series for instance, had very limited manufacturer support. In such cases, FORTH has been graded with very high marks.” To which he adds the following comment: “On the other hand, we in the FORTH community face a very real problem, in that, as the manufacturers have provided increased quality in software, the need and demand for FORTH appears to diminish. FORTH was providing some irreplaceable attributes five years ago. Now it appears that a number of those attributes are no longer as attractive as they were. For example, there is more memory space available, I/O is faster, more disk space is available, file structures are less limiting. This puts an increased challenge on people using and writing FORTH systems. Are they going to stay back in the “crippled computer” mentality, or are they going to continue to grow and follow industry needs?”
Despite the industry changes, FORTH holds a unique position as a complete self-contained software interface to the computer hardware.
Here is one diagram you can create for yourself. It shows how the user invokes the hardware to invoke the software to invoke a function that returns an answer via the software and the hardware, back to the user.
SCR # 4 0 -------- 1 | FORTH | 2 ------ | | 3 | | |--------| 4 | USER | | applic | 5 | | | | ------- 6 ------ |--------|->| MATH | 7 | A | devel | |-------| 8 | | | | | arith | 9 V | |--------| |-------| 10 ------ | system | | logic | 11 | |->| | |-------| 12 | HARD | |--------|<-| comp. | 13 | WARE | | assem | ------- 14 | |<-| | 15 ------ --------
Here is another diagram for you. It may look like a chocolate layer cake, but really spells out the different layers that must be present in any working software interface to any computer hardware system. In fact, the diagram really shows why FORTH is FORTH—why FORTH is unique and can do all those marvelous things that no other software interface can.
SCR # 4 0 1 LAYERS IN THE SOFTWARE 2 INTERFACE 3 ____________________ 4 | | 5 | applications layer | 6 |____________________| 7 | | 8 | development layer | 9 |____________________| 10 | | 11 | systems layer | 12 |____________________| 13 | | 14 | assembler layer | 15 |____________________|
The applications layer represents the applications programs and systems with which most users of personal (and other) computers are familiar. Word processing, database management, spread sheets, graphics packages, accounting systems and spelling verifiers are a few examples.
On the development layer, we have the tools that a programmer uses to develop his applications, including a slew of languages, from FORTRAN, COBOL, PL/1 and PASCAL to SNOBOL, APL and LISP (Lots of Irritating, Superfluous Parentheses!) to one of the latest and the one with the shortest name, C (which, in turn followed from the B language written by computer chess expert Ken Thompson in 1970). On the development layer, we also have such programs as editor and debugging packages.
At the systems layer, the software has to do cold and warm starts, as well as talk to the files and the peripherals (which go under such names as OS, DOS, MS-DOS, PC-DOS, UNIX, VENIX and XENIX.
Underneath all these layers lies the assembly (or machine language) layer, which consists simply of 0s and 1s, and is the only language that the computer hardware can understand.
The ultimate justification for FORTH is that it spans all these layers, can do what all these layers are supposed to do and, in addition, can do—at the level of a high-level language—what no other software can. If FORTH were just another high-level computer language or just another operating system, there would be little reason to take the time to learn it; the advantages of compactness, speed and virtual storage have been neutralized to some extent by advances in computer hardware over the past seven years. Smaller and faster chips, however, have had no impact on our unique ability to remain in FORTH and float up, down and back from the applications to the development, to the systems and assembly layers as we choose—and within a single definition.
Our simple little program to put a letter F on the screen serves not only as an excellent demonstration of structured coding without GOTOs, but also of the multiple layers of FORTH.
The applications layer, of course, is our demonstration of the word FORTH on the screen.
The development layer is clearly visible on the disk screen where we created the original program by the use of the editor and the associated debugging features.
The systems layer can best be appreciated by executing the DECOMP instruction in either valFORTH or Team Atari FORTH (which makes this FORTH a worthwhile investment). If we do DECIMAL DECOMP STAR, we get approximately the following:
5AE6 : LIT 42 5AEA : EMIT 5AEC : ;S
If we do DECOMP STARS, we get a clear illustration of how the systems layer of FORTH takes our coding from the development layer and converts it into executable code. This is what we get:
5AF8 : 0 5AFA : (DO) 5AFC : STAR 5AFE : (LOOP) TO 23292 5B02 : ;S
Here FORTH’s systems layer inserts the forward and backward branches that are needed for the specified iterations of the DO loop. You can DECOMP the rest of the words for a better appreciation of the FORTH systems layer.
How about the assembly layer? For that, we need some extra apparatus. We need a way to see what the assembler code looks like. We can do this easily with nothing more than the instruction DECIMAL 200 TYPE, which will type out (beginning at address 0) the next 200 bytes of memory. This output is hard to read. We can improve readability by creating a few simple dump routines to display memory in graphics characters, or by bytes in decimal or hexidecimal. In these examples, AAA is the starting address and BBB the ending address.
: DUMPG BBB AAA DO I 1 TYPE SPACE LOOP ; : DUMPD BBB AAA DO I C@ . SPACE LOOP ; : DUMPK BBB AAA DO HEX I C@ . DECIMAL LOOP ;
Let us now dump the assembly code for our letter F program. The address of the code will be different, depending on what FORTH we are using, and how many words are in our dictionary. The general pattern, however, remains the same. To find the starting address, we only need ' STAR . (pronounced “tick STAR dot”). From this information we can arrive at the starting and ending addresses for the dump.
In the dump, we see the names of the words in our program: STAR, STARS, MARGIN, BAR, BLIP and F. Each word begins with a so-called head, which contains an indicator of its length in bytes, followed by the name of the word, then a pointer to the previous word (to allow dictionary searches), followed by a code field address (points to the code to be executed) and, finally, a “body” with one or more parameter field addresses (these point to previously defined words which we included in the definition of our original word).
The byte structure of each word is a topic that is longer than you can afford to include in your demo at this stage. There is one important point, however, which must not be overlooked. Dr. C.H. Ting of the Lockheed Missiles and Space Company wrote a book called Systems Guide to fig-FORTH ($25.00, Mountain View Press, P.O. Box 4656, Mountain View, CA 94040), which is the only one I have seen designed specifically to “deal with the inner mechanisms on how the FORTH system operates, which is essential to the understanding and effective utilization of the FORTH language.” He notes that “the FORTH language is a major synthesis of many of the concepts and techniques used for some time in the computer industry, such as stacks, dictionary, virtual memory and interpreter. The single most important invention by Charles Moore in developing this language, which wrapped all these elements and rolled them into a small but powerful operating system, is the code field in the header of a definition. The code field contains the address of a routine to be first executed when the definition is called.”
In the May issue of Forth Dimensions, he appends, “Code fields and the associated inner interpreters are the sole inventions Mr. Charles Moore brought us in FORTH. Stacks, the dictionary, indirect threaded code and virtual memory were all well-developed techniques before FORTH was invented. Using the code field to identify a specific interpreter to execute a particular command was not obvious or considered useful prior to that time. The code field sets FORTH apart from any other type of language or programming constructs, and it is the most unique feature in FORTH or FORTH-like systems. Many of the attributes associated with the FORTH language, such as compactness, simplicity and extensibility, can only be realized with the use of the code field.”
The idea to keep in mind is that the CFA or code field address plays a central role in the operation of FORTH. To know how FORTH works, we must have a clear picture of the function CFA performs when it interacts with other FORTH components. A proper understanding should, as Dr. Ting says, “be able to cut through much of the mythical fog often surrounding FORTH.”
No demo of FORTH, however, should get bogged down in a discussion of points of theory, no matter how important. So let us add some final fireworks to keep our audience psyched up.
This program will put a colorful pattern on the screen:
: MOIRE 24 GR. 1 0 14 SETCOLOR 2 0 0 SETCOLOR 318 0 DO 1 COLOR 159 0 PLOT I 191 DRAWTO 3 +LOOP 30000 0 DO LOOP ;
If you have QS Forth (and Ekkehard Floegel’s book Forth for the Atari), note that the inputs to the SETCOLOR statement are reversed, while PLOT and DRAWTO have the COLOR statement embedded. Instead, do this:
: MOIRE 24 GR. 14 0 1 SETCOLOR 0 0 2 SETCOLOR 318 0 DO 159 0 1 PLOT I 191 1 DRAWTO 3 +LOOP 30000 0 DO LOOP ;
This program provides a flashy display:
SCR # 6 0 ( color display 6/30 } 1 DECIMAL : DELAY 0 DO LOOP 2 : CENTER 3 712 C@ 710 C@ 712 C! 4 709 C@ 710 C! 709 C! ; 5 : FLASH 100 0 DO CENTER 6 1000 DELAY LOOP ; 7 : BACKGROUND 200 0 DO I 712 C! 8 1000 DELAY 2 +LOOP ; 9 : FOREGROUND 200 0 DO I 710 C! 10 1000 DELAY 2 +LOOP ; 11 : WELCOME 125 EMIT 10 10 POS. 12 ." Welcome to Forth " 13 FLASH BACKGROUND FOREGROUND 14 0 GR. ;
Be sure to tell your audience to watch for the next exciting installment in the continuing sage of the wonderful world of FORTH!
A.N.A.L.O.G. ISSUE 25 / DECEMBER 1984 / PAGE 24
Few things in life are harder than hitting a round ball with a curved bat. Baseball coaches who do their homework can make it seem easy. Some young folks get the knack. Some few grow up to be experts. Then they make more money in a year than the President of the United States. Dave Winfield of the New York Yankees earned more than $2 million last year.
If you want to demonstrate FORTH, remember the coach: you have to make it look easy. One way is to present FORTH as a language, even though it’s more than that. It is also an operating system and can be a complete software interface between the hacker and his hardware. But FORTH can be thought of as a language for program development.
If FORTH is a language, it must has words. And every language has to have words which do things (called “verbs”) and words which name things (called “nouns”). What are the verbs in FORTH? What are the nouns? And which should we tackle first?
If we are to demo FORTH to our friends, we should present the nouns first, because that makes it easy for them to start writing simple programs. Before we do that, though, let’s show them what the verbs look like. How? All it takes is one word: VLIST.
On your screen you’ll get a listing of all the words in the FORTH dictionary. You can pause the listing with CTRL 1. These are all the words you will need to know to master FORTH. Most of them are written in FORTH, except for a few at the bottom of the list, which are written in assembly language. The majority are verbs, because in FORTH a “word” is used to refer to a command, instruction or executable procedure.
When you talk about the dictionary in FORTH, you’re usually referring to the FORTH vocabulary. But there are other vocabularies as well—particularly the editor and assembler vocabularies.
To see them, you first have to load the editor or assembler code. Then type EDITOR DEFINITIONS followed by VLIST Typing FORTH DEFINITIONS will restore the original VLIST.
If you scan the output from VLIST, you will immediately recognize a few of the verbs of FORTH. There is the plus sign (+) for addition, the minus sign (-) for subtraction, the asterisk (*) for multiplication and the slash (/) for division. (What the Mock Turtle in Alice in Wonderland called “the different branches of Arithmetic—Ambition, Distraction, Uglification, and Derision” And, of course, VLIST)
Where are the nouns? The answer is: you create them yourself What are they? They can be numbers, letters or strings of characters. How do you create them? Where do you put them? That brings us to the topics of your demo for today.
People have been standing in line for aeons, so mathematicians developed what they call the queueing theory, which they describe as the area of stochastic (random) processes, emphasizing those processes modeled on the situation of individuals lining up for service.
The theory studies two kinds of queues: a queue like a line at the box office, called a “first-in, first out” or “FIFO” queue; and a “last-in, first-out” or “LIFO” queue, usually called a “stack.” Some common stacks we all recognize are: a card deck with the top card turned over, the row of shopping carts in the grocery store, Tom Hudson’s descriptive stack of pancakes (Boot Camp, issue 20), or the balls in a can of tennis balls. The last ball in the can is at the top-of-stack (or “TOS”), and there is no way in the world (without breaking the can) to get at the bottom ball without removing each of the others in turn.
A stack is easier to manage than a queue. You only need a pointer to keep track of the top of the stack and a counter to record the depth.
What does a stack have to do with FORTH? Actually, everything. FORTH is built around the dictionary (and editor) for verbs and the stack for nouns. (The other key elements of FORTH are the return stack, the text and inner interpreters, the assembler and disk memory—all good topics for future demos.)
Whenever you create a noun in FORTH, it goes on the top of the stack and stays there until you retrieve it. Mastery of the stack is the key to mastery of FORTH. Some words in FORTH do not affect the stack, but most of them either take or remove data from the stack. A few of them do both.
The stack (also known as the “data stack” or “parameter stack”) is the main reason for FORTH’s power and flexibility. As you create new procedures, you can pass data backward and forward by means of the stack. There are no parentheses in FORTH (except to enclose your comments), and the stack is what makes it possible to get rid of the parentheses.
There is a list processing language called LISP that offers some competition to FORTH in the field of AI (artificial intelligence). It has no stack and, consequently, uses so many parentheses that some jokers will tell you that LISP stands for Lots of Irritating Silly Parentheses.
You have to get used to the FORTH stack. Not because it’s difficult to use, but because of the way you were brainwashed in elementary school. You were told that 2 plus 3 makes 5. What the teacher should have said is: “Take 2, then take 3, and add them together.”
If you type:
2 3 + .
FORTH will put the 2 on the stack, then the 3. The “word” + takes the two top items from your stack, adds them together and puts the answer on the top of the stack. The final . (dot) takes the number off the top of the stack and prints it.
If you want to calculate (3+4)*(5 +6), you will say to yourself: “Take 3 and 4 and add them, then take 5 and 6 and add them, then multiply the answers.” FORTH does it exactly the same way:
3 4 + 5 6 +
and comes back with 77 OK.
Here is what the stack looks like if you calculate (4+7)*(9-3) and enter it in FORTH notation:
3 7 9 9 6 4 4 11 11 11 11 66 ---------------------- 4 7 + 9 3 - * . 66 OK
Fortunately, the stack can be displayed with ease at any time by the S word, which is found in QS Forth, Team Atari Forth and valFORTH. In Team Atari Forth, the command STACKON will display the stack at the top of the screen in color. In valFORTH, the command ON STACK displays the stack after each OK.
With the stack and some simple arithmetic commands, you can use your FORTH computer as a handheld calculator.
22 33 + 44 + . 99 OK 55 22 - . 33 OK 12 144 * . 1728 OK 1728 12 / . 144 OK 1730 12 /MOD . . 144 2 OK
You can even demonstrate exponentiation with this code:
: ** 1 - -DUP IF OVER ROT ROT 0 DO OVER * LOOP SWAP DROP THEN ;
so that you get:
2 14 ** . 16384 OK
if you want to multiply two by itself fourteen times. You can also use your FORTH computer as a programmable calculator. For example, to evaluate the polynomial 5x2−7x−10, you could use this code:
: POLY1 DUP DUP * 5 * SWAP 7 * - 10 - . ;
which would give you:
75 POLY1 27590 OK 90 POLY1 -25676 OK
Or, if you wanted the value of this expression for various values of y (2y4+3y3+4y2+5y+6), then this code will work:
: SQUARED DUP * ; : CUBED DUP SQUARE * ; : 4THPOWER DUP CUBED * ; : POLY2 DUP DUP DUP 4THPOWER 2 * SWAP CUBED 3 * + SWAP SQUARED 4 * + SWAP 5 * + 6 + . ;
so that, for a value of two, we get:
2 POLY2 88 OK
Four factorial is four times three times two times one. Here is a factorial program:
: FACTORIAL 1 + 1 1 ROT ROT DO I * LOOP ;
which will give us:
7 FACTORIAL 5040 OK
and here is another program to find the square root of an unsigned number:
: PICK 2 * SP@ + @ ; : SQUAREROOT 1 BEGIN OVER 8 0 PICK U/ SWAP DROP OVER + 0 2 U/ SWAP DROP SWAP OVER = UNTIL SWAP DROP ;
which we can test with:
32400 SQUAREROOT 180 OK
To work with the stack, as you have seen, we must bear in mind the tennis balls in a can. We need verbs in FORTH that will let us add, change or delete data from the stack.
Deleting is easy. DROP removes the top item from the stack this way:
2 3 4 5 DROP DROP . . 3 2 OK
A quick way to wipe the stack clean is to type an imaginary word (like XX), which gives an error message and, at the same time, clears the stack.
To add items to the stack, we can use DUP, -DUP, OVER and PICK—like this:
3 4 DUP DUP . . . . 4 4 4 3 OK 0 -DUP ( dupe if nonzero ) . 0 OK 2 3 OVER . . . 2 3 2 OK 4 5 2 PICK . . . 4 5 4 OK
To move items on the stack, we use SWAP, ROT and ROLL.
ROLL, like PICK, is not in fig-FORTH but can be defined as follows:
: ROLL DUP 1 = IF DROP ELSE DUP 1 DO SWAP R> R> ROT >R >R >R LOOP 1 DO R> R> R> ROT ROT >R >R SWAP LOOP ENDIF ;
Here are some examples that only move the items on the stack:
2 3 4 SWAP . . . 3 4 2 OK 4 5 6 ROT . . . 4 6 5 OK 7 8 9 3 ROLL . . . 7 9 8 OK
You must be aware during your demo that the stack will only handle 16 bits at a time. With this number of bits, we can only represent integer numbers from -32768 to +32767. One of the bits is used for the plus or minus sign. The above examples will give you wrong answers if you work with numbers outside this range. If we do not need the sign bit, then we can handle unsigned values from 0 to 65,535.
If you need larger numbers than this, you can work with the two top items on the stack, which will give you 32 bits. These can represent signed numbers between -2,147,483,648 and +2,147,483,647 (or from 0 to 4,294,967,295, if unsigned). To do this, however, you will need a different set of FORTH words. There are floating point packages which allow you to handle numbers with the same ease as in BASIC. They are not part of the public domain fig-FORTH, but exist in vendor offerings such as valFORTH.
One point that is often overlooked in FORTH is brought out by C. Kevin McCabe in his book FORTH Fundamentals, Vol. 1 ($16 from Mountain View Press, PO. Box 4656, Mountain View, CA). He gives the most complete explanation to date of the basics of fig-FORTH. On page 39, he says:
How does FORTH distinguish between signed and unsigned characters on the stack or in memory? Quite simply, it doesn’t The characteristics of each value exist only in the programmer’s mind, in selecting operations where the value is an appropriate operand. FORTH does not keep track of the kind of values represented, nor does it prevent or check for range errors. The burden is on you, the programmer, to insure that the contents of the stack and memory are those intended and that they represent values of the correct type.
In other words, the 16 binary digits at the top of the stack mean whatever you say you want them to.
This raises another point. You can display numbers on the stack in binary, octal, decimal or hexadecimal. In fig-FORTH, in fact, you can choose any base between 2 and 36. For example:
HEX 100 DECIMAL . 256 OK HEX 10 DECIMAL . 16 OK
However, as McCabe points out: “It must be emphasized that the numeric base is only used during the conversion of values sent to and from the terminal. Internally, all values will still be in binary…”
The number on the stack can also represent an address. If you type:
0 1000 TYPE
then FORTH will interpret the 0 as an address and start to type out the character equivalent of the first 1000 bytes in memory.
The number on the stack can also be used to represent a logical flag, as with this code
: TRUEORFALSE IF ." True" ELSE ." False" ENDIF ;
so that can make a test for false if zero and true otherwise:
20 TRUEORFALSE True OK 0 TRUEORFALSE False OK -2 TRUEORFALSE True OK
We can also use the stack to store the ATASCII code of keyboard characters, with this code:
: ATASCII KEY CR DUP EMIT SPACE . ;
which will wait for a key press and then print the code, as well as the corresponding character:
ATASCII ( type a Z ) Z 90 OK
The stack can also be used to handle bytes—equivalent to 8 of the 16 bits on the stack. The words C! and C@ store and fetch 1 byte at a time to and from memory; they merely pad the other 8 bits with zeros. The assembler command C moves bytes from the stack to the dictionary.
At this point, your audience should begin to appreciate the two principal features of FORTH. First, you write programs by expanding the dictionary (hence, the language) with your own verbs. Second, FORTH can use a stack (instead of named variables, as other languages do) to pass your nouns—or data—from one word (or subprogram) to another. If FORTH code appears less readable than the code in other languages, this is a small price to pay for ease in developing fast and efficient programs.
The power and flexibility of the stack brings with it the responsibility—that falls on you—to insure that everything is done correctly. Much of the effort of learning FORTH consists of memorizing the stack input and output parameters of the respective FORTH words.
In the code below, you will notice that PLOT and DRAWTO and POS. all expect two numbers on the stack, while SOUND looks for four.
What we have here is a small “wake up” demo to start your presentation. Remember that any character (except BACKSPACE, BLANK and the RETURN key) can be used as a FORTH word. Some tricks were used to save space, such as the use of the caret (^), vertical stroke (|) and underscore (_), as well as lower case letters and, finally, the redefinition of PLOT as PL and DRAWTO as DR.
Note, also, the rising sound on each letter—it will help to identify the double consonants. The demo will work on both Team Atari Forth and valFORTH versions of fig-FORTH, so you can modify it to suit your tastes. QS Forth requires a minor change to screen 1. The number in the COLOR statement must be added to each PLOT or DRAWTO so remove 3 COL OR in Line 4 and change Line 6 to:: PL 3 PLOT;: DR 3 DRAWTO;
There are two other “last-in, first-out” stacks in FORTH. They are the return stack and the dictionary itself But, if you want to make FORTH took easy, you’ll have to save those demonstrations for another day.
SCR #1 0 ( analog stack 1 ) 1 DECIMAL 250 VARIABLE NOTE 2 : ^ 30000 DO LOOP 3 0 0 0 0 SOUND 2 GR. ; 4 : | 2 GR. 3 COLOR NOTE @ 3 - 5 NOTE ! 0 NOTE @ 10 8 SOUND ; 6 : PL PLOT ; : DR DRAWTO ; 7 : a | 3 9 PL 3 0 DR 15 9 DR 8 6 6 PL 12 6 DR ^ ; 9 : d | 5 PL 5 3 DR 12 9 DR 10 15 6 DR 15 3 DR 12 0 DR 11 5 0 DR ^ ; 12 : e | 13 0 PL 5 0 DR 5 3 DR 11 13 4 PL 5 4 DR 5 3 DR 13 3 DR ^ ; 14 : f | 13 0 PL 5 0 DR 5 3 DR 15 11 4 PL 5 4 OR 5 9 DR ^ ; --> SCR #2 0 ( analog stack 2 ) 1 : h | 5 0 PL 5 3 DR 13 0 PL 2 13 9 DR 5 4 PL 13 4 DR ^ ; 3 : i | 9 0 PL 9 9 DR ^ ; 4 : k | 5 0 PL 5 9 DR 6 3 PL 5 13 DR 6 4 PL 13 9 DR A ; 6 : l | 5 0 PL 5 9 DR 13 9 DR ^ ; 7 : n | 5 9 PL 5 0 DR 13 9 DR 8 13 0 DR ^ ; 9 : o | 5 0 PL 5 9 DR 13 9 DR 10 13 0 DR 5 0 DR ^ ; 11 : p | 5 9 PL 5 0 DR 13 0 DR 12 13 4 DR 5 4 DR ^ ; 13 : r | 5 9 PL 5 0 DR 13 0 DR 14 13 4 DR 5 4 DR 13 9 DR ^ ; 15 --> SCR #3 0 ( analog stack 3 ) 1 : s | 13 0 PL 5 0 DR 5 4 DR 2 13 4 DR 13 9 DR 5 9 DR ^ ; 3 : t | 5 0 PL 13 0 DR 9 PL 4 9 9 DR ^ ; 5 : u | 5 0 PL 5 9 DR 13 9 DR 6 13 0 DR A ; 7 : w | 5 0 PL 5 9 DR 9 6 DR 8 13 9 DR 13 DR ^ ; 9 : y | 5 0 PL 9 5 DR 9 9 DR 10 9 5 PL 13 DR ^ ; 11 : _ ^ ; 12 --> 13 14 15 SCR #4 0 ( analog stack 4 ) 1 : MESS2 p e o p l e _ w h o _ 2 t h i n k _ t h e y _ k n o w 3 _ i t _ a l l _ a n n o y _ 4 t h o s e _ o f _ u s _ w h o 5 _ r e a l l y _ d o ^ 0 GR. ; 6 : MESS3 7 10 5 POS. ." PEOPLE WHO THINK" 8 10 10 POS. ." THEY KNOW IT ALL" 9 10 15 POS. 10 ." ANNOY THOSE OF US" 11 12 20 POS. ." WHO REALLY DO" 12 20 23 POS. QUIT ; 13 14 : MESSAGEONE NOTE @ 250 NOTE ! 15 MESS2 MESS3 ;
A.N.A.L.O.G. ISSUE 26 / JANUARY 1985 / PAGE 18
To demonstrate FORTH on the Atari, you’ll want to stress the best features of each. Atari’s superb graphics came to the forefront again when Computer Animation Primer at last hit the bookstores. Those who bought Atari for its graphics capabilities—then waited two years for this work by David Fox and Mitchell Waite—were well rewarded for their patience.
The first half of the book gives a vivid look at state-of-the-art computer-generated animated movies. The second half is all Atari: animation with character sets and color registers, player/missile graphics, scrolling and display lists, and more than fifty pages of Atari BASIC programs. The authors chose to work solely with the Atari computer because of its unique potential for graphics and animation.
The color registers on the Atari, as they put it, give programmers amazing animation capabilities, even in BASIC. “Unbeknownst to many people, the Atari 400/800 contains color-mapping hardware (called color registers), and this feature alone gives it awesome capabilities when compared to its competitors.” The book culminates in the Great Movie Cartoon—a scrolling street scene with five layers of depth, plus cars and strolling people in the foreground.
FORTH stands at the forefront of the second generation of computer software—where the user writes his own software, instead of going through a middleman. Elimination of the GOTO construct (with its resulting spaghetti code) and the subsequent realization that all programs could be constructed with three structures (sequential, two-way branch and “do while”) led to top-down design for large systems. Users could begin to exercise control.
FORTH, although it first achieved prominence in the days before cheap megabyte memories and nanosecond cycles as “an operating system for crippled micros,” is moving with the new trend, along with LOGO in the educational field, and UNIX and the C language on the mainframes and minis. FORTH, with no GOTO, is ideally suited for structured programming, top-down program design and bottom-up program compilation. The user can build upon past software to solve new problems as they arise.
The easy way for you to introduce Atari users to FORTH is via the chapter called Color Graphics and Sound in Bob Albrecht’s Atari BASIC, which probably came wrapped in the same box with their Atari computer—and certainly belongs on the bookshelf of every Atari owner
The Italians have a saying, “Tradutore, traditore,” that the translator is a traitor But many BASIC statements have straightforward FORTH counterparts.
If you organize a demonstration for your user group, you might find a teammate who will display the BASIC code on one machine while you display the corresponding FORTH code on another.
The chapter on color graphics and sound provides a large number of illustrations. For example, 10 GR. 3 becomes 3 GR. This short program:
10 GRAPHICS 3 20 PRINT "LINE 1" 30 PRINT "LINE 2" 40 PRINT "LINE 3" 50 PRINT "LINE 4" 60 GOTO 60
translates as follows:
: P285 ( for page 285 ) 3 GR. CR ." LINE 1" CR ." LINE 2" CR ." LINE 3" CR ." LINE 4" QUIT ;
The code 60 GOTO 60 gets rid of the READY, and QUIT gets rid of the OK. (Which brings to mind an oldie-BASIC to FORTH: “I’m READY! You’re OK!”)
This program plots a point:
: P286 3 GR. 2 5 PLOT ." This is plot position 2,5" ;
and this one plots four points:
: P287 3 GR. 1 COLOR 5 5 PLOT 15 5 PLOT 5 15 PLOT 15 15 PLOT ;
Remember, however, if you have QS Forth, or the Extended fig-FORTH written by Patrick L. Mullarky and once sold by APX, that both PLOT and DRAWTO take COLOR as their third input parameter. So, in this (and other) examples, you would have to use this code:
: P287 3 GR. 5 5 1 PLOT 15 5 1 PLOT 5 15 1 PLOT 15 15 1 PLOT ;
This loop plots a line of points:
FOR I=0 TO 19 STEP 2:PLOT I,I:NEXT I
but in FORTH we must add 1 to the loop index:
: P289 3 GR. 1 COLOR 20 0 DO I I PLOT 2 +LOOP ;
This program displays a large X:
: P291 4 GR. 1 COLOR 0 0 PLOT 78 39 DRAWTO 78 0 PLOT 0 39 DRAWTO ." X marks the spot" ;
This program features an endless loop (until you hit an interrupt key) to replace 60 GOTO 60 and the use of I as the index for the loop:
: P292 4 GR. BEGIN 2 0 DO I COLOR 0 40 DO 0 0 PLOT 78 I DRAWTO -13 +LOOP LOOP ?TERNINAL UNTIL ;
Here is the SETCOLOR statement:
: P293 3 GR. 1 COLOR 0 0 2 SETCOLOR 0 3 PLOT 38 3 DRAWTO ." What color do you see?" ;
and here SETCOLOR uses the loop indexes, while we use an empty DO…LOOP to create a delay. Notice that J is a FORTH-79 word you may not have, which copies the third item of the return stack onto the parameter stack, and thus lets you access the index of the outer loop. It comes in handy for two-dimensional arrays.
: DELAY 30000 0 DO LOOP ; : J >R >R >R R R# ! >R >R >R R# @ ; : P295 3 GR. 1 COLOR ." Watch my colors change" 5 0 DO 16 0 DO J I 2 SETCOLOR 5 5 PLOT 30 5 DRAWTO DELAY LOOP 2 +LOOP ;
Try this as a simple animation demo of a man approaching you.
: P296 3 8 DO I GR. 1 COLOR 4 1 2 SETCOLOR 5 5 PLOT 7 5 DRAWTO 6 6 PLOT 6 18 DRAWTO 4 15 DRAWTO 6 18 PLOT 8 15 DRAWTO 5 7 PLOT 3 9 DRAWTO 7 7 PLOT 9 9 DRAWTO DELAY -2 +LOOP ." What a funny man!!!" ;
The next program draws rectangles in three sizes and shows how this code:
PLOT X-2,Y-2: DRAWTO X*2,Y-2
can be done, using FORTH constants:
80 CONSTANT X 48 CONSTANT Y : P297 7 GR. 1 COLOR 0 15 2 SETCOLOR X Y PLOT X 2 - Y 2 - PLOT X 2 + Y 2 - DRAWTO X 2 + Y 2 + DRAWTO X 2 - Y 2 + DRAWTO X 2 - Y 2 - DRAWTO X 4 - Y 4 - PLOT X 4 + Y 4 - DRAWTO X 4 + Y 4 + DRAWTO X 4 - Y 4 + DRAWTO X 4 - Y 4 - DRAWTO ;
We can combine three graphics modes in one program, as well as having the use of both variables and constants:
1 VARIABLE N 0 CONSTANT X 0 CONSTANT Y : P298 BEGIN 1 ' M ! 3 8 DO I GR. 1 COLOR 0 1 2 SETCOLOR 80 M @ / ' X ! 48 M @ / ' Y ! X Y PLOT X 2 - Y 2 - PLOT X 2 + Y 2 - DRAWTO X 2 + Y 2 + DRAWTO X 2 - Y 2 + DRAWTO X 2 - Y 2 - DRAWTO X 4 - Y 4 - PLOT X 4 + Y 4 - DRAWTO X 4 + Y 4 + DRAWTO X 4 - Y 4 + DRAWTO X 4 - Y 4 - DRAWTO DELAY M @ 2 * ' M ! -2 +LOOP ?TERMINAL UNTIL ;
Here, incidentally, is a familiar screen from your Atari BASIC Reference Manual.
: P51 3 GR. 0 2 8 SETCOLOR 1 COLOR 17 1 PLOT 17 10 DRAWTO 9 18 DRAWTO 19 1 PLOT 19 18 DRAWTO 20 1 PLOT 20 18 DRAWTO 22 1 PLOT 22 10 DRAWTO 30 18 DRAWTO 1 752 C! CR 6 SPACES ." Atari Personal Computers" 30000 0 DO LOOP QUIT ;
This brings us to the end of Bob Albrecht’s section on graphics. As you can see, the FORTH version of the BASIC code produced relatively few surprises. The next section of the book explores Atari’s four voice capabilities, providing us a chance to show off some new techniques.
First of all, you want a word to shut off the four voices at any time, just as END will do in BASIC.
: QUIET 4 0 DO I 0 0 0 SOUND LOOP ;
The valFORTH fix—for those of us who cannot remember that SOUND looks for channel, frequency, distortion and volume—is CatFish Don’t Vote. This next program allows you to experiment with loudness by furnishing an integer from 0 to 15, and -5 to end it.
0 VARIABLE L : INPUT# QUERY CR ?TERMIHAL CR INTERPRET ; : P299 BEGIN INPUT# L ! L @ -5 = NOT IF 0 115 10 L @ SOUND ELSE 0 0 0 0 SOUND QUIT THEN ?TERMINAL UNTIL ;
You will find frequent use for the word INPUT# because many BASIC interactive programs ask for a signed integer from the keyboard. The following loop increases the loudness, step by step:
: P300 16 0 DO 0 115 10 I SOUND DELAY LOOP QUIET ;
and this loop varies the distortion levels:
: P300B 16 0 DO 0 115 I 8 SOUND DELAY LOOP QUIET ;
This program runs through all of the notes with a short time delay:
: DELAY_BRIEF 1000 0 DO LOOP ; : P300C 256 0 DO 0 I 10 8 SOUND DELAY_BRIEF LOOP QUIET ;
Notice that you can use the underscore PL/1 style to separate words, as in DELAY_BRIEF, which looks neater than the COBOL-style hyphen.
The READ…DATA construct is used repeatedly in BASIC. The translation to FORTH is very easy…once you know how. Suppose we want to define an array with four locations, holding initial single precision values of 12, 24, 48 and 96, and want to print the last two. Here is the code:
12 VARIABLE GROUP 24 , 48 , 96 , GROUP 4 + @ . <ret> 48 ok GROUP 6 + @ . <ret> 96 ok
This word will fetch the nth element of the array:
: GET-GROUP ( stack: nl -- n2 ) 2 * ( find byte offset ) GROUP ( leave parm field start ) + ( calculate abs location ) @ ; ( fetch value ) 0 GET_GROUP . <ret> 12 ok 3 GET_GROUP . <ret> 96 ok
With this technique, we can store the notes of a musical scale in a table and read them at will:
DECIMAL 145 VARIABLE DATA 129 , 122 , 109 , 97 , 92 , 82 , 73 , 65 , 61 , 54 , 48 , 46 , 41 , 36 , 32 , : WAIT_LONG 30000 DO LOOP ; : WAIT_SHORT 1000 DO LOOP ; : GET_DATA 2 * DATA + @ ; : SCALE ( p. 302 ) 5 GR. 16 0 DO 1 COLOR 0 I GET_DATA 10 8 SOUND 12 SETCOLOR 5 11 PLOT 70 11 DRAWTO WAIT_LONG 0 0 10 8 SOUND WAIT_SHORT LOOP ;
If we want to display warm colors with high notes and cold colors with low notes, we can use a similar technique. The trick here is to alternate the numbers in the table and retrieve them in pairs:
DECIMAL 122 VARIABLE DATA 9 , 109 , 10 , 97 , 13 , 92 , 15 , 82 , 1 , 73 , 2 , 65 , 3 , 61 , 5 , : GET_NOTE 4 * DATA + @ ; : GET_COLOR 4 * DATA 2 + + @ ; : P302B 3 GR. 8 0 DO 1 COLOR 0 I GET_COLOR 2 SETCOLOR 10 9 I - PLOT 28 9 I - DRAWTO 0 I GET_NOTE 10 8 SOUND WAIT_LONG 0 0 2 SETCOLOR 0 0 10 8 SOUND WAIT_SHORT LOOP ;
Here is the way to handle GOSUB and RETURN within a FORTH program, and play three notes:
0 VARIABLE N : GOSUB100 0 N @ 10 8 SOUND WAIT_LONG 0 0 10 8 SOUND WAIT_SHORT ; : P303 122 N ! GOSUB100 109 N ! GOSUB100 97 N ! GOSUB100 ;
You can play a one-octave scale and plot notes on the screen in color, using nested subroutines. Here is a way to do it with two FORTH screens:
SCR 1 0 VARIABLE N VARIABLE X 0 VARIABLE Y : GOSUB200 30000 DO LOOP ; : GOSUB100 X @ Y @ PLOT 0 N @ 10 8 SOUND GOSUB200 ; --> SCR 2 : P304 3 GR. 1 COLOR 1 10 2 SETCOLOR 14 5 DO 0 I PLOT 38 I DRAWTO 0 15 PLOT 7 15 DRAWTO ( 30 15 PLOT 35 15 DRAWTO ) 2 +LOOP GOSUB200 4 4 2 SETCOLOR 2 COLOR 3 X ! 15 Y ! 122 N ! GOSUB100 5 X ! 14 Y ! 189 N ! GOSUB100 7 X ! 13 Y ! 97 N ! GOSUB100 9 X ! 12 Y ! 92 N ! GOSUB100 11 X ! 11 Y ! 82 N ! GOSUB100 13 X ! 10 Y ! 73 N ! GOSUB100 15 X ! 9 Y ! 65 N ! GOSUB100 17 X ! 8 Y ! 61 N ! GOSUB100 QUIET ;
You can show off two of the four voices with the following:
8 VARIABLE L0 VARIABLE L1 41 VARIABLE N0 129 VARIABLE N1 : GOSUB1000 0 N0 @ 10 L0 @ SOUND 1 N1 @ 10 L1 @ SOUND 30000 0 DO LOOP ; : P304B GOSUB1000 8 L1 ! GOSUB1000 0 L0 ! GOSUB1000 0 L1 ! GOSUB1000 ;
Here is your chance to demonstrate a simple tune with two voices, using these three FORTH screens:
SCR 20 0 VARIABLE N0 0 VARIABLE N1 : DELAY50 5000 0 DO LOOP ; : DELAY100 10000 0 DO LOOP ; : DELAY150 15000 0 DO LOOP ; : GOSUB1000 0 N0 @ 10 8 SOUND 1 N1 @ 10 8 SOUND DELAY50 ; : GOSUB2000 0 N0 @ 10 8 SOUND 1 N1 @ 10 8 SOUND DELAY100 ; : GOSUB3000 0 N0 @ 10 8 SOUND 1 N1 @ 10 8 SOUND DELAY150 ; --> SCR 21 : 1STHALF 41 N0 ! 129 N1 ! GOSUB2000 32 N0 ! 109 N1 ! GOSUB1000 27 N0 ! 92 N1 ! GOSUB1000 31 N0 ! 97 N1 ! GOSUB2000 32 N0 ! 109 N1 ! GOSUB1000 36 N0 ! 122 N1 ! GOSUB3000 41 N0 ! 129 N1 ! GOSUB1000 36 N0 ! 122 N1 ! GOSUB1000 32 N0 ! 109 N1 ! GOSUB1000 36 N0 ! 122 N1 ! GOSUB3000 27 N0 ! 92 N1 ! GOSUB2000 0 N0 ! 0 N1 ! GOSUB1000 41 N0 ! 129 N1 ! GOSUB2000 --> SCR 22 : 2NDHALF 32 N0 ! 109 N1 ! GOSUB1000 25 N0 ! 192 N1 ! GOSUB1000 36 N0 ! 122 N1 ! GOSUB2000 32 N0 ! 109 N1 ! GOSUB1000 31 N0 ! 97 N1 ! GOSUB1000 32 N0 ! 109 N1 ! GOSUB1000 36 N0 ! 122 N1 ! GOSUB1000 27 N0 ! 92 N1 ! GOSUB2000 41 N0 ! 129 N1 ! GOSUB1000 32 N0 ! 109 N1 ! GOSUB1000 36 N0 ! 122 N1 ! GOSUB1000 41 N0 ! 129 N1 ! GOSUB1000 0 GR. ; : P305 1STHALF 2NDHALF QUIET ;
Now you see how Atari’s sound statements can be demonstrated in FORTH.
One important prerequisite for many Atari games is a random number generator to simulate chance events, such as a throw of the dice or a turn of the cards. Fortunately, Atari has a random number generator built into the hardware, in location 53770 (or 520A in hex), which will give you a random number between and 255. In BASIC, all you need is:
10 PRINT PEEK(53770)
but in FORTH, it’s better to define a word that will give a number between and a chosen upper limit. For example, 10 RND# will give a random number from 0 to 9. If you want a range of, say 5 to 15, then just add 5 to the result. Here is a definition which divides by the number at the top of the stack, drops the quotient and keeps the remainder as the random number:
: RND# ( stack: n1 -- n2 ) 53770 ( hardware address ) C@ ( fetch the byte ) SWAP ( swap for divide ) /MOD ( divide by n1 ) DROP ( discard quotient ) ; ( and keep reitainder )
You will find many uses for this simple and elegant random number generator.
What about computer animation? Well, that brings us to the point of this whole story. David Fox says (on page 35) that, “if you want to write computer games for personal computers that effectively use animation…you need to know a high-level computer language…You should also look at FORTH and C, two high-speed languages that are now available for many personal computers…You might also need to understand something called bit-slice microprocessors, as well as the FORTH language. FORTH is a tricky, powerful, exclusive (bordering on religious) language that is also extremely fast.”
A few pages later, he adds, “You may also want to investigate FORTH as a graphics language. Although it is rather difficult to learn, it is a somewhat elegant language, and your own graphics instructions are easily added to it. Its advantages include high speed, immediate execution of programs (no compilation like in Pascal and C), ability to define your own commands, and very compact code.”
So let us take David Fox at his own word and start translating some of his BASIC programs into FORTH. Here are a few for openers. If you manage to translate some of the others, you will have the makings of a spectacular demo.
To simulate an explosion, you can flash ten random colors on the background with this BASIC code:
180 FOR I=1 TO 10 200 POKE 712,RND(0)*255:NEXT I 300 POKE 712,0:REM FLASH BACKGRND
which you can render in FORTH as:
: DELAY 1000 DO LOOP ; : EXPLODE 19 GR. 10 1 DO 255 RND# 712 C! DELAY LOOP 712 C! ;
This program will flash through all the screen colors so fast that, if you leave out the delay loop, you’ll hardly be able to see them. Here it is, in BASIC:
100 GRAPHICS 3+16 200 REM STEP THROUGH EVERY COLOR 300 FOR I=0 TO 254 STEP 2 400 POKE 712,I:REM CHANGE BACKGRND 500 FOR W=1 TO 50:NEXT W 600 NEXT I:GOTO 200
and here’s the FORTH version:
: DELAY100 1000 0 DO LOOP ; : FLASH 19 GR. 254 0 DO I 712 C! DELAY100 2 +LOOP ;
Here’s an airplane, wingtip lights flashing, heading toward you in a lightning storm:
10 GRAPHICS 3+16 20 COLOR 2 30 PLOT 10,8 40 COLOR 1 50 DRAMTO 29,8 60 COLOR 3 70 PLOT 30,8 80 COLOR 1 90 PLOT 20,5:PLOT 28,6 100 PLOT 19,7:DRAWTO 21,7 110 PLOT 19,9:DRAWTO 21,9 120 SETCOLOR 1,3,6:REM MAKE RED 130 SETCOLOR 2,12,6:REM MAKE GREEN 140 FOR I=1 TO 50:NEXT I:REM PAUSE 150 SETCOLOR 1,0,0:REM MAKE BLACK 160 SETCOLOR 2,0,0:REM MAKE BLACK 170 FOR I=1 TO 400 180 IF RND(0)*20<1 THEN SETCOLOR 4,0,1 4:SETCOLOR 4,0,0:REM RANDOM LIGHTNING FLASH 190 NEXT I 200 GOTO 120
Here’s the FORTH counterpart:
: SE. SETCOLOR ; : SEC DELAY100 ; : AIRPLANE_IN_STORM 19 GR. 2 COLOR 10 8 PLOT 1 COLOR 29 8 DRAWTO 3 COLOR 30 8 PLOT 0 COLOR 20 8 PLOT 1 COLOR 20 5 PLOT 20 6 PLOT 19 7 PLOT 21 7 DRAWTO 19 9 PLOT 21 9 DRAWTO BEGIN 1 3 6 SE. 2 12 6 SE. SEC 1 0 0 SE. 2 0 0 SE. 400 1 DO 200 RND# 10 < IF 4 0 14 SE. 4 0 0 SE. ENDIF LOOP ?TERMINAL UNTIL ;
As David Fox says, “We will show you how to bring the exciting world of animation into your own home. If you have an Atari microcomputer, you will be able to turn your computer into a fabulous animation machine.”
A.N.A.L.O.G. ISSUE 27 / FEBRUARY 1985 / PAGE 26
The best way to demonstrate FORTH at the beginning of a new year is to present a program to draw calendars. This will be useful. You can add your own heading, print it on your printer and then distribute the calendar to promote your demonstration.
More importantly, the program serves as an enlightening example of how to structure a FORTH program to cope with a commonplace but tricky calculation. The program is in the public domain, thanks to Jesse Jay Wright of Pasadena, California, who contributed it to the November 1983 issue of Forth Dimensions.
The calendar problem is simply this: to match one of the seven days of the week to the day of the month and year. Since there are only seven days in the week, there are only seven yearly calendars of 365 days—because New Year’s Day falls on one of them. However, leap year adds a second set of seven 366-day years, for a total of fourteen different yearly calendar setups. You can make a so-called perpetual calendar with a simple table of years to point you to one of the fourteen yearly calendars. This, of course, is a cop-out; we will try another way.
The first question to be answered is whether we’ve got a leap year or what is known as a “common” year. The leap year dates to the time of Julius Caesar, when all years had 365 days. By then, the civil equinox differed from the astronomical by three months, so that the winter months were carried back into autumn, and the autumnal months into summer. Caesar tied the year to the rotation of the earth around the sun, once every 365¼ days, with a leap year every fourth year.
Actually, the solar year is only 365 days, 5 hours, 48 minutes and 46 seconds, so that by 1582 the calendar was again ten days out of synch. Pope Gregory then abolished the ten days from October 5th to 14th, and eliminated the leap years in three of every four century-years.
The British government waited until the calendar was eleven days out of line, then imposed the Gregorian calendar in England and America in 1752. Now we have the anomaly that Washington was not born on Washington’s birthday. On his birthday (1731), the calendar read February 11, not February 22.
Pope Gregory’s calendar is good until the year 4000, and skipping the leap year will make it good for another 3,000 years.
Here is the FORTH code to test for a leap year:
: IS_LEAP_YEAR? (year--flag) DUP DUP 400 MOD 0= SWAP 100 MOD 0= NOT OR SWAP 4 MOD 0= AND ;
First we put the year on the stack, then duplicate it twice to test for the century leap years. (Incidentally, you can enter the code on the screen line by line, pressing RETURN after the end of each line; your Atari will continue to compile the FORTH code until the final semicolon.) The swap makes the flag second on the stack, and the last line tests for divisibility by four. We can check the code as follows:
1983 IS_LEAP_YEAR? . 0 ok 1984 IS_LEAP_YEAR? . 1 ok 1985 IS_LEAP_YEAR? . 0 ok
The second question is: how many days are there in the month? My teacher told me to count the long and short months on the knuckles of the left hand, beginning with January on the index finger, March on the middle finger, and back to the index finger with August. Others prefer: “Thirty days hath September, April, June and November…”
On a computer, the solution is to set up a table like the one below:
: CTABLE <BUILDS 0 DO C, LOOP DOES> + C@ ; 31 30 31 30 31 31 30 31 30 31 29 31 0 31 30 31 30 31 31 30 31 30 31 28 31 0 26 CTABLE DAYS_IN_MONTH ( month -- days in month )
We can test it easily enough by giving the number of any month:
1 DAYS-IN-MONTH . 31 ok 11 DAYS_IN_MONTH . 30 ok 2 DAYS_IN_MONTH . 28 ok
This innocent-looking table is actually an example of the most sophisticated feature of FORTH—and a monument to the genius of its inventor, Dr. Charles H. Moore. FORTH, unlike any other language, lets you create your own structures, so that you aren’t bound by the restrictions imposed by the architects of BASIC and Pascal and FORTRAN and the rest, but can build the language to suit your own needs. In this respect, FORTH stands alone.
In the words of Chuck Moore: “If you are taking a course in computers which is taught in Pascal, you mostly don’t learn about computers; you learn about Pascal. You learn about how a certain class of people think programs should be written and algorithms defined. But you don’t learn much about the computer underneath the software. Take the same course in FORTH. FORTH is more transparent, and you would focus more closely on the problem, on the ways of solving it, than on the theory of computation…FORTH lets you get directly to that part of the problem instead of getting bound up in compilers and subroutines and things which aren’t of fundamental interest.”
The <BUILDS DOES> construct lets you build your own structures. How? You don’t lead a blind man to the cliff and then walk away. Here is a brief, one-paragraph explanation which you can offer your audience as a downpayment on the full-page explanation to which they are entitled. The words <BUILDS and DOES> allow you to create new structures which have both a compile-time and a run-time behavior. If, for instance, you define the word
: VECTOR <BUILDS , , DOES> 2@ ;
then, during compilation, a phrase such as
41 270 VECTOR WIND
will place an entry named WIND in the dictionary and store (using , , or comma comma) two values after the name. (By the way, 2@ in fig-FORTH is DUP 2+ @ SWAP @.) When the program is run, then WIND will place the two numbers 41 and 270 on the stack. In our case, <BUILDS uses a DO…LOOP to create the 26-byte table, and DOES> lets DAYS_IN_MONTH retrieve the proper byte. That is the story in a tiny nutshell.
The third question we must answer is: what is the day of the year, given the day, month and year? Here is where we use both the leap year routine and the table of days in each month. You can, if you need to, replace 2DUP with OVER OVER and 2DROP with DROP DROP, which are actually the fig-FORTH versions of two words belonging to the FORTH-79 double number extension word set.
: DAY_OF_YEAR ( day, month, year -- day of year) IS_LEAP_YEAR? IF 13 + 14 ELSE 1 ENDIF 2DUP = IF 2DROP ELSE DO I DAYS_IN_MONTH + LOOP ENDIF ;
What the code does is test for a leap year to convert the offsets in the table, or else start at month one. The second IF looks for January and returns the day, or else loops to cumulate the days in each of the preceding months. We can test the program this way:
1 1 1983 DAY_OF_YEAR . 1 ok 31 12 1984 DAY_OF_YEAR . 366 ok 31 12 1985 DAY_OF_YEAR . 365 ok
Now we are in a position to answer the question we posed in the first place: what is the day of the week, given the day, month and year? All we need do is cumulate the number of days from, say, New Year’s Day in 1900 (which was a Monday) and then divide by seven to get the day of the week (counting up from Sunday as zero).
: DAY_OF_WEEK ( day, month, year -- day of week ) ( where sunday is 0) DUP >R DAY_OF_YEAR 0 R> 1900 DO I IS_LEAP_YEAR? IF 366 + ELSE 365 + ENDIF LOOP + 7 MOD ;
Here we save the year on the return stack, get the current day of the year, add 365 or 366 days for each year since 1900, and then take the remainder after dividing by seven. Since we are dealing with signed numbers, we can cumulate a maximum of 32,767 days or a little more than 89 years from whichever year we choose as the starting point in which New Year’s Day falls on a Monday (such as 1951, 1962 or 1973).
The code makes clever use of the stack. A stack diagram comes in handy now, just to make everything clear.
FORTH code has often been criticized as difficult to read. “FORTH programs tend to be dense and hard to read,” according to Charles F. Taylor in his Master Handbook of High-Level Microcomputer Languages. He says, “understanding a FORTH program written by someone else often requires a great deal of tedious effort to track stack effects, and so on. Some would call FORTH a ‘write only’ language.”
The solution is a technique to keep track of the stack, such as the one in The Complete Forth by Alan Winfield. “I promised to describe a technique for illustrating the stack during program execution, and it is just such a technique which we would use to clarify the operation…The technique is to list vertically each word in the body of the colon definition. Then look up each word, in turn, in the FORTH Handy Reference, noting that word’s stack effect. The author has found this stack notation invaluable in developing FORTH programs with complex stack manipulations and, far from being cumbersome, the technique soon becomes rapid as familiarity is gained. In particular, the experienced FORTH programmer will not have to refer often to the Handy Reference, and will place words…in groups of more than one, where the stack effect is very clear (or none at all), so that the whole diagram is much simplified.”
WORD STACK ACTION : DAY_OF_WEEK 31 12 1984 (day,month,year--day of week) DUP 31 12 1984 1984 (add a copy of year to stack) >R 31 12 1984 (store year on return stack) DAY-OF-YEAR 366 (change date to day of year) 0 366 0 (zero a counter to sum the days) R> 366 0 1984 (recover year from return stack) 1900 366 0 1984 1900 (beginning year for count) DO 366 0 (limit & index on return stack) I 366 0 1900 (push index to stack) IS_LEAP_YEAR? 366 0 flag (set true flag for leap year) IF 366 * 366 total-days (add 366 to total-days if true) ELSE 365 + 366 total-days (add 365 to total-days if false) ENDIF LOOP + total-days (count days since Jan l, 1988) 7 MOD ; ( 31046 / 7 = 1 for a Monday)
Since we can now pinpoint the days of the week. we can print out a calendar for any month in any appropriate year.
: CALENDAR ( Month, year--) ( print calendar for Month ) 2DUP SNAP 18 SPACES . ." - " . CR ." Sun Mon Tue Wed" ." Thu Fri Sat " CR 2DUP 1 ROT ROT DAY_OF_WEEK DUP 2* 2* SPACES ROT ROT IS_LEAP_YEAR? IF 13 + ENDIF DAYS_IN_MONTH 1+ 1 DO I 4 .R 1+ DUP 7 = IF CR ENDIF 7 MOD LOOP 0= NOT IF CR ENDIF CR ;
To test it, we can try:
12 1984 CALENDAR
which produces a calendar that looks like this:
12 - 1984 Sun Mon Tue Wed Thu Fri Sat 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 28 21 22 23 24 25 26 27 28 29 30 31
We now have all the inputs to print a calendar for a whole year with just a twelve-month loop:
: CALENDAR-YEAR ( year -- ) 13 1 DO DUP I SWAP CALENDAR LOOP DROP ;
And there you have it! You can dress it up any way you please. You might want to set up a table of constants to let you input the name of the month, instead of the number (JANUARY CONSTANT 1…). Or a table to print the name, rather than the number of the month. You might also like to add a message at the end or at the beginning.
No demonstration, however, can be complete without an illustration of Atari’s lasting claim to fame—superb color graphics.
Two years ago. Herb Kohl and Ted Kahn brought out a book called Atari Games and Recreations, an engaging 338-page tutorial on Atari and BASIC, complete with comic book cartoons. One feature recreates a fable of the George (Animal Farm) Orwell of 2500 years ago, the Greek slave Aesop, who told of “the classic race between the Tortoise and the Hare. The old, plodding, deliberate Tortoise is challenged to a race by the speedy Hare. The Tortoise is steady, regular and sometimes painfully slow. The Hare is fast but erratic. It jumps all around, is impatient and, in the original Greek fable, is foolish. The Hare runs too fast for itself…”
Here is a FORTH version which will execute in any fig-FORTH with 1 LOAD 2 LOAD 3 LOAD TORTOISE, and needs no commentary. As the lawyers say, res ipse loquitur—“the thing speaks for itself.”
SCR #1 ( tortoise and hare df841016) : RND# ( random number ) 53770 C@ SWAP /MOD DROP ; : J ( index of outer loop ) R> R> R> R R# >R >R >R R# @ ; : CLEAR_SCREEN 125 EMIT ; : DELAY 0 DO LOOP ; : INITIAL 7 GR. 1 COLOR CLEAR-SCREEN ." A race!! " ." Tortoise versus the Hare!!" CR 3000 DELAY ." On your marks, get set," ." GO ! ! !" CR 3068 DELAY ; ;S SCR #2 ( tortoise and hare df841016) : COMMENTS ( checks column # ) DUP 10 = IF CR ." Place your bets, folks !!!" CR ENDIF DUP 40 = IF CR ." Want to change your bets???" CR ENDIF DUP 65 = IF CR ." It's down to the wire...!!!" CR ENDIF DUP 75 = IF CR ." And the winner is...???" CR ENDIF 77 = IF CR ." Guess Who...!!!" CR ENDIF ; ;S SCR #3 ( tortoise and hare df841816) : FINALE CLEAR_SCREEN ." Once again, " ." ORDER triumphs over CHAOS!" QUIT ; : TORTOISE INITIAL 79 0 DO 75 0 DO ( tortoise ) J I PLOT J I 4 + DRAWTO J ( col # on stack ) COMMENTS ( hare ) 79 RND# 80 + 79 RND# PLOT 79 RND# 80 + 79 RND# DRAWTO 5 +LOOP LOOP FINALE ; ;S
A.N.A.L.O.G. ISSUE 29 / APRIL 1985 / PAGE 13
A short list or array of numbers can make an interesting game, as well as an instructive FORTH demo. Many of us met FORTH arrays for the first time on page 196 of Leo Brodie’s first book, Starting Forth, in which he has a lab with five burners to heat various kinds of liquids. Here, “we can make our word ?TOO.HOT check that all five burners have not exceeded their individual limit.”
He defines the five limits with:
0 VARIABLE LIMITS 8 ALLOT
which sets up five slots for the array, and then stores a number in the first one with:
220 LIMITS !
and the second one with:
348 LIMITS 2 + !
and then defines a new word:
: LIMIT 2* LIMITS + ;
to take the burner number off the stack and store a limiting temperature for the third burner with:
170 2 LIMIT !
Brodie then defines a new word:
: ?TOO.HOT ( burner# temp -- ) LIMIT @ > IF ." Danger - reduce heat " THEN ;
which works like this:
300 1 ?TOO.HOT ok 350 1 ?TOO.HOT Danger - reduce heat ok
Kevin McCabe, the Chicago lawyer who wrote Forth Fundamentals (the most complete explanation to date of fig-FORTH), also shows (page 119) how to set up an array with four locations with:
12 VARIABLE GROUP 24 , 48 , 96 ,
and an auxiliary word:
: GETGROUP 2* GROUP * @ ;
that will retrieve any member of the array with:
0 GETGROUP . 12 ok 3 GETGROUP . 96 ok
In both cases, the count starts at zero and there is no checking for errors.
“As an exercise in array manipulation,” M.P. Burton composed a public domain FORTH version of the number game Reverse, which was published in Forth Dimensions magazine for January 1982 . The object of the game is to arrange a list of numbers (1 through 9) in ascending order from left to right. Moves are made by reversing a subset of the list (from the left). The original game was written in BASIC almost ten years ago by Peter Sessions of People’s Computer Company, a nonprofit educational corporation in San Ramon, California.
Our first screen spells out the object of the game.
SCR # 1 : OBJECT ." The object of the " ." game is" CR ." to arrange a" ." random list" CR ." of nine" ." numbers into ascend-" CR ." ing numerical order in" CR ." as few moves as possible" CR ." by reversing a subset of " CR ." the list. For example, " CR ." given the random list, " CR ." 5 2 4 8 7 3 9 1 6 " CR ." reversing a subset of 4 " CR ." would yield the list, " CR ." 8 4 2 5 7 3 9 1 6 " CR ." To finish the game, " CR ." simply reverse 0 " ; ;S
We also need a flag to tell whether the player wants to continue. The handy word Y/N (which can be used in many games) asks for an input string from the console, which is stored in PAD, the location of the first byte of the scratchpad area that moves upward as the dictionary expands. An upper case or lower case Y returns a true flag.
: Y/N ( c -- flag ) PAD 80 EXPECT PAD C@ CR CR 95 AND 89 = ;
Now we can ask the player at the beginning whether he wants instructions for the game, and at the end, whether he wants to play again. This code invokes the word OBJECT, if requested:
: INSTRUCTIONS CR CR 10 SPACES ." The game of REVERSE" CR CR ." Would you like" ." instructions?" Y/N IF OBJECT THEN ;
Our first task will be to set up a ten-number array, and then initialize it with the numbers 1 to 9 (element 0 will not be used). We can reserve an integer word array with:
: DIM ( n --) <BUILDS (reserve an integer word array) 1+ 2 * ALLOT DOES> ;
Defining words in FORTH execute by compiling new word definitions into the dictionary. Two examples are CONSTANT and VARIABLE, which will store named single-precision numeric values. The far more powerful defining word : (pronounced colon) permits compilation into the dictionary of word definitions using a “building block” approach. The word : (colon) uses ; (semicolon) as a terminating word. Later execution of a colon-defined word is equivalent to the execution of each of its building blocks.
If you ever wondered about the choice of : and ; as FORTH words, this is the reason…When Charles Moore developed FORTH, he started out with:
DEFINE: Name something something END;
but found it tiresome to keep typing DEFINE: and END; each time, so he shortened them to save himself (and us!) many keystrokes.
FORTH also allows you to create new high-level defining words. Such words may then be executed to create new classes of words, like numeric arrays or double-precision variables and constants. You will find a discussion of user-defining words in chapter eleven of Leo Brodie’s Starting Forth. Remember, however, that he uses the series CREATE…DOES>. In fig-FORTH, this should be <BUILDS…DOES>.
The word DIM, which we just created, is such a word. It’s a defining word in the form n DIM xxxx, which looks for a number on the stack, and then produces an n+1-length word array named xxxx, which we can then fill with elements through n. We do this with:
9 DIM ARRAY
which reserves a ten-word array. Now we need a word to store numbers in the array, which we can do with:
: A! ( store an array element ) ( array-value index -- ) 2 * ARRAY + ! ;
We can then use a DO…LOOP to initialize our array with the numbers J to 9, in order:
: AINIT ( initialize the array) 10 1 DO I DUP A! LOOP ;
Just as we needed a word to store numbers in the array, we’ll need words to fetch the array elements and to place them on the data stack—and, also, to print them. The fetch word, which looks like the store word is:
: A@ ( fetch an array element ) 2 * ARRAY + @ ;
and the print word becomes:
: A. ( print the array ) CR ." The list is now..." CR 6 SPACES 10 1 DO I A@ 3 .R LOOP ;
To start the game, we must present the player with a scrambled list of numbers. Since the game was written in FORTH as an exercise in array manipulation, we are right in supposing that this code will be the heart of the game. Most games require a random number generator, and this one is no exception.
0 VARIABLE SEED : RND ( random number generator) ( range -- rnd# ) SEED @ 259 * 3 + 32767 AND DUP SEED ! 32767 */ ;
This is a pseudorandom number generator (there is a difference, as we’ll see), courtesy of Forth Dimensions. RND generates a number in the range 0 through -1 and is used to scramble the number list.
: ASCRAMBLE ( mix up array values) 1 9 DO I RND 1+ ( calculate K ) I A@ ( get ARRAY(I) value) OVER A@ ( get ARRAY(K) value) I A! ( store ARRAY(K) ) ( in ARRAY(I) ) SWAP A! ( store ARRAY(I) ) ( in ARRAY(K) ) -1 +LOOP ;
The code, as you see, counts down the array from 9 to 1 (0 is ignored) and uses RND to calculate a value K, with which to swap each of the numbers into a different slot.
Since the object of the game is to get the numbers in the right order (in effect, to neutralize or unscramble the results of ASCRAMBLE), we need a word which will check that ARRAY has been placed in the proper numerical order and, if so, will return a true flag.
: ACHECK ( ascending sequence? ) ( -- flag ) 1 10 1 DO I DUP A@ = AND LOOP ;
This routine puts a 1 (or true flag) on the stack, which remains true (1 AND 1 equals 1) as long as each comparison is true, but switches to false (0 AND 1 equals 0) if any one fails.
Because Reverse is an interactive game, we need a way to get input from the player. We must solicit the number of elements in the list that he wants to reverse (which reminds me of page 1 of IBM’s tutorial manual on their computer SYSTEM/38: “This manual follows the convention that HE refers to HE or SHE”). The code features a continuous loop, so that, if the player enters any character other than 0 through 9, the program issues an error message and loops back to ask for another number.
: GETIN ( get amount to reverse ) ( -- n ) BEGIN CR ." Reverse how many? " PAD 80 EXPECT PAD @ 48 - DUP 0< OVER 8 > OR DUP IF CR ." Only 0 thru 9 allowed. " THEN 0= UNTIL CR ;
You might notice that the ATASCII code for a 0 is decimal 48, and for a 9 is decimal 57, so that we must subtract 48 from the ATASCII code to get the number we want.
The other crucial part of the game (and the other exercise in array manipulation) is a routine to reverse a leftmost subset of the array, starting with element number 1. The code is similar to ASCRAMBLE, except that it rearranges a part of the array, instead of the complete array from 1 to 9.
: AREVERSE ( reverse a subset) ( n -- ) DUP 2 / ( loop limits are ) 1+ 1 ( 1 to [n/2]+1 ) DO DUP ( calculate index ) I - 1+ ( n-I+1 ) DUP A@ SWAP ( get ARRAY[n-I+1]) I A@ ( get ARRAY[I] ) SWAP ( store ARRAY[I] ) A! ( in ARRAY[n-I+1]) I ( store ARRAY[n-I+1]) A! ( in ARRAY[I] ) LOOP DROP ;
We need two more pieces of the puzzle, then we’re ready to put the game together. There is a variable:
0 VARIABLE MOVES
which keeps track of the number of reverses so far. We also need a keypress to start up the random number generator, as follows:
: SKEY CR ." Please depress any key:" KEY SEED ! ;
Now we’re ready for the game definition, which uses all the previously defined words, mostly in the order in which we defined them.
: REVERSE ( play the game ) 125 EMIT SKEY INSTRUCTIONS AINIT BEGIN ASCRAMBLE 0 MOVES ! BEGIN A. GETIN DUP 0= IF 1 ELSE AREVERSE 1 MOVES +! ACHECK THEN UNTIL A. CR ." You made " MOVES @ . ." reversals." CR CR ." Care to play again? " Y/N 0= UNTIL CR ." Thanks for playing " ." REVERSE..." CR CR ; ;S
And there we have it. The code will fit in seven or eight screens without crowding. You can end each screen with a ;S (semi-S or stop), a fig-FORTH runtime procedure that’s available for separate use, if needed (FORTH-79 doesn’t specify names for its run-time procedures, which are assumed to be inaccessible to the user). You can then load screens one at a time. If everything loads correctly, you can use a final screen (for example, screen 9) as a LOAD screen (1 LOAD 2 LOAD 3 LOAD 4 LOAD… ;S), so that 9 LOAD will load the entire game.
How do you play the game to beat a competitor? Mostly by trial and error. There are three different strategies. According to Peter Sessions, there is the algorithmic approach and the heuristic approach. The first is to adopt the strategy of a computer program: move the 9 to the right, then the 8, etc. With two reversals for each of the nine numbers, that should never take more than eighteen moves. Burton’s only comment about the game is: “If more than fifteen moves are made to win, you haven’t got the hang of the game.”
The second approach is by rule of thumb, seeking to take advantage of partial orderings in the list. This is the way most people play.
The third way, of course, is to “cheat.” This isn’t meant to advocate dishonesty, but merely to make sure ahead of time that, if one plays a game, one is familiar with the rules and plays by them.
The rules aren’t always what they seem. There’s a story about Alan Mathison Turing (Ph.D., Princeton, 1939), the eccentric British mathematician, who worked as Britain’s chief cryptographer and managed to crack the German cypher code during World War II, by building one of the earliest computers (the Germans didn’t find out until thirty years later).
He wanted to become proficient with a rifle, so he enrolled in the wartime Home Guard. The form read: “Do you understand you place yourself liable to military law?” Turing, characteristically, answered no instead of yes. Once he became an excellent shot, he stopped attending parades. When the apoplectic colonel called him to task, Turing said, “I am not a soldier…look at my form.” He had been improperly enrolled. It was typical of Turing at all times to look for the optimal strategy. His life story in the new book by Andrew Hodges, Alan Turing: The Enigma, reads like a classic Greek tragedy. Although he did more than any one man to win the war for Britain, he died unrecognized (a suicide) in 1954, at age 41.
Notice that, in this game. Burton uses a pseudorandom number generator. These come in two flavors—those that repeat and those that don’t. The repeating ones are the most useful in scientific experiments. If you’re running a computer simulation of, say, a grocery store, to find out how many shopping carts you need, and use random numbers to model the times between customers’ arrivals, you want to be able to repeat the experiment for three, four and five checkout counters using exactly the same numbers.
This game asks a keypress to seed the random number generator. Most people, out of laziness, hit the SPACE BAR. The game will then keep churning out the same sequence of scrambled numbers each time it is called. Someone who took the trouble to work out the answers ahead of time would have a significant advantage.
Of course, the number of possible lists is a large number. For example, the 9 can appear in any of nine slots, then the 8 in any of the remaining slots, and so on. The answer is 9 times 8 times 7…times 1, or 9 factorial (9!). You can work it out for yourself with a program something like this:
: FACTORIAL 1 + 1 1 ROT ROT DO I * LOOP ;
which gives us:
7 FACTORIAL 5040 OK
In this particular instance, however, we need some double-precision words:
: 2SWAP >R ROT ROT R> ROT ROT ; : PICK 2* SP@ + @ ; : ROLL DUP 1= IF DROP ELSE DUP 1 DO 5MAP R> R> ROT >R >R >R LOOP 1 DO R> R> R> ROT ROT >R >R SWAP LOOP THEN ; : S->D DUP 0< MINUS ; : 2@ DUP 2+ @ SWAP @ ; : 2! SWAP OVER ! 2+ ! ; : D* OVER 5 PICK U* 6 ROLL 4 ROLL * + 2SWAP * + ;
The new factorial program becomes:
: 2VARIABLE <BUILD5 4 ALLOT DOES> ; 1. 2VARIABLE PRODUCT : 2FACTORIAL 1. PRODUCT 2! 1 + 1 1 ROT ROT DO PRODUCT 26 I S->D D* PRODUCT 2! LOOP CR PRODUCT 2@ D. ;
which will then give us:
9 2FACTORIAL 362880 OK
Poker players always ask for a new deck in the original wrapping, then look for markings on the backs by flipping the deck and riffling through the cards. We could accomplish something similar by swapping random number generators, of which there are several we can. choose from.
This one appears on page 265 of Brodies book:
1 VARIABLE RND HERE RND ! : RANDOM RND @ 31421 * 6927 + DUP RND ! ; : CHOOSE ( u1 - u2 ) RANDOM U* SWAP DROP ;
Alan Winfield’s The Complete Forth, on page 113, has:
0 VARIABLE RND 1234 RND ! : RANDOM RND 6 1021 * 41 * DUP RND ! ;
which places a new random number on the stack and which, he warns, repeats itself every 65535 numbers, “but that should be no problem here!” The following variation appears on page 65 of Forth Tools by Anderson and Tracy:
0 VARIABLE SEED 1234 SEED ! : RAND ( -- n ) SEED @ 5421 * 1+ DUP SEED ! ; : RANDOM ( n -- random number ) RAND SWAP MOD ;
The Atari hardware generates a true random number at location 53770, which you can use like this:
: RND# 53770 C@ SWAP /MOD DROP ;
or in the more elaborate version that appears on page 41 of Ekkehard Floegel’s book, Forth for the Atari:
0 VARIABLE RND 53770 @ RND ! : RANDOM RND @ 31421 * 6972 * DUP RND ! ; : RND# ( n1 -- n2 ) RANDOM U* SWAP DROP ;
You can sum it up for your audience this way…Play Reverse. Have fun! Check the keyboard and pick a key at random, instead of the SPACE BAR. Don’t be lazy!?
A.N.A.L.O.G. ISSUE 30 / MAY 1985 / PAGE 19
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 CT ! 0 C ! 4 0 2 SETCOLOR 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 10 */ 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.
A.N.A.L.O.G. ISSUE 31 / MAY 1985 / PAGE 19
You are now in a position to take over the lectern as a FORTH professor and teach a course to beginners.
The equipment you will need includes: (1) the class textbook—Alan Winfield’s 130-page paperback, The Complete Forth, from Wiley Press (605 Third Ave., New York, NY 10158); (2) your favorite Atari fig-FORTH; (3) the accompanying road map to insure a safe and comfortable journey for all; and (4) a hat you can pass around that’s large enough to hold, in change, the $16 you laid out for the book. If you’re ready and your audience is ready, we can start right in.
Chapter one is devoted to FORTH fundamentals. On page 5, since this is a book about FORTH-79, you encounter NEGATE, which you can define with this code:
: NEGATE MINUS ;
You then encounter PICK (page 7), which can be defined as:
: PICK 2 @ SP@ + @ ;
and then ROLL, which requires:
: ROLL DUP 1 = IF DROP ELSE DUP 1 DO SMAP R> R> ROT >R >R >R LOOP 1 DO R> R> R> ROT ROT >R >R SWAP LOOP ENDIF ;
Here, incidentally, is a philosophical observation on PICK and ROLL made by Leo Brodie in his new book on systems analysis using FORTH, called Thinking Forth:
Some folks like the words PICK and ROLL. They use these words to access elements from any level on the stack. We don’t recommend them. For one thing, they encourage the programmer to think of the stack as an array, which it is not…Second, they encourage the programmer to refer to arguments that have been left on the stack…without being explicitly passed as arguments….That’s unstructured—and dangerous. Finally, the position of an element on the stack depends on what’s above it, and the number of things above it can change constantly…Code like this is hard to read and harder to modify
Chapter two describes a simple model of a FORTH system as it executes a line of input. You must remember (page 13) that VARIABLE in fig-FORTH, just like CONSTANT, requires an input parameter, so you must write the word VARIABLE as VARIABLE—unless, of course, you choose to add this definition:
: VARIABLE 0 VARIABLE ;
Redefined words, as Winfield notes on page 17, may generate an error message, provided you’ve previously added the error messages to your working disk.
The colon definition is the topic of chapter three. He points out that “a colon definition may occupy more than one line of input and, even though we type a ‘return’ at the end of each line, FORTH does not complete the definition and print ‘ok’ until after the terminating semicolon.”
On some FORTH systems, he says (page 27), CREATE cannot be used in this way, and VARIABLE must be used instead. This means you must replace:
CREATE array 40 ALLOT ok CREATE TABLE -10 , -5 , 0 , 5 , 10 , ok
with this fig-FORTH code:
0 VARIABLE MYARRAY 40 ALLOT Ok -10 VARIABLE MYTABLE -5 , 0 , 5 , 10 , ok
On page 28, you may want to use .DEPTH to print the depth of the stack, instead of .S, which has probably been predefined in your FORTH to give a nondestructive stack print. On that page, you can redefine 2- as:
: 2- 2 - ;
Winfield’s definition of CREATE, you must bear in mind, holds for FORTH-79 but not for fig-FORTH.
FORTH structures are introduced in chapter four, beginning with IF. On page 33, you may want to replace his line:
FORTH: A @ = IF ." A=2" THEN
with this code:
0 VARIABLE A 2 A ! : A@ A @ = IF ." A=2" THEN ;
which will execute correctly. The range test (page 35) will work with this definition:
0 VARIABLE X : TESTX X @ 10 > IF X @ 100 < IF ." yes" THEN THEN ;
so that 55 X ! TESTX will give ." yes" but any number outside that range will merely return ok.
Here is another example of AND on that page that you might wish to add:
DECIMAL 77 ok 99 ok BINARY .S ( print stack) 1001101 1100011 ok AND . 1000001 ok
The range test further down can be defined as:
: RANGETEST DUP 0 < SWAP 100 > OR IF ." no" THEN ;
At the end of this chapter, you must redefine ?DUP with:
: ?DUP -DUP ;
FORTH loops are covered in chapter five. You can print as many as 256 squares beginning with 1, 4, 9,…(page 43) if you change . (dot) to U., but you will overflow if you try 257.
The word J is needed (page 45) to access the index of the outer loop, since it is not defined in fig-FORTH, although your version may have it.
: J R> R> R> R R# ! >R >R >R R# @ ;
The multiplication table will work with 3 .R for proper spacing on Atari’s 32-character line (instead of Winfield’s 64-character line). The next page refers to &TERMINAL, which looks for the break key in Team Atari Forth, but looks for one of the three yellow keys in valFORTH. Since your computer uses Atari ASCII (or ATASCII instead of ASCII), you may want to substitute 65 (for A) or 90 (for Z) in place of the 32 (ASCII space) that Winfield uses on page 47. The next page requires this definition:
: 0> 0 > ;
Note, in closing this chapter, that the word ABORT, which corresponds to a warm start, gives the fig-FORTH message.
The sixth chapter discusses saving and loading programs. You may want to skip his section 6.2, The Editor, since you have your own editor, and pick up with his section 6.3, More Block Handling. On page 59, you should specify a 32-character print line (instead of 64) in the definition of INDEX.
You can define the word DATA with:
0 VARIABLE DATA 80 ALLOT
which you can then clear with:
DATA 40 32 FILL
or fill with alphabetic characters, with:
: DATA# 40 0 DO I 65 + DATA I 2 * + ! LOOP ;
You will also need:
: MOVE CMOVE ;
Then you can choose an unused screen at the end of the disk, say screen 60, to define:
: SAVEDATA DATA 60 BLOCK DATA 40 MOVE UPDATE ; : LOADDATA 60 BLOCK DATA 40 MOVE ;
If you then fill DATA with blanks or alphabetic characters, you can execute SAVEDATA and then examine screen 60 to verify that the data was moved. Conversely, you can edit the beginning of screen 60, execute LOADDATA and then use DATA 40 TYPE to examine the reverse movement. If you execute 60 CLEAR, followed by LOADDATA and DATA 40 TYPE, then DATA should be filled with blanks.
If you are using valFORTH, make sure that your blocks are defined as 1024 bytes and not 512.
The basics of character input and output, as well as number input, are covered in chapter seven. You must replace Winfield’s definition of:
: CR 13 EMIT 18 EMIT ;
with this Atari version:
: CR 155 EMIT ;
and, to set tabs, you need to change:
: TAB 9 EMIT ;
: TAB 127 EMIT ;
and, to clear the screen, instead of:
: CLRS 12 EMIT ;
: CLRS 125 EMIT ;
Incidentally, valFORTH uses CLS for the same command.
The fig-FORTH version of CREATE requires:
: CREATE 0 VARIABLE -2 ALLOT ;
as Winfield indicates in his footnote, so that you must define STRING as follows, and can use 4 ALLOT in place of 6 ALLOT (page 66):
0 VARIABLE STRING 4 ALLOT
For the Atari, you will need to redefine GETSTR in this fashion:
: GETSTR 6 0 DO KEY STRING I + C! LOOP ;
and then you can print the string with PRINTSTR. In fig-FORTH, you need to change WORD to read WORD HERE, so that the PRINTNEXT definition now becomes:
: PRINTNEXT 32 WORD HERE COUNT TYPE ;
which is what the footnote (page 67) suggests. As Glen B. Haydon notes in All About Forth in a comment: “Care must be taken in moving source code from fig-FORTH to FORTH-79, which includes the ideogram WORD. In FORTH-79, WORD leaves the address of HERE on top of the stack. Also, while in fig-FORTH, the string is stored at HERE; in FORTH-79 another buffer may be used.”
Once again, the STRING on page 68 should be defined as:
0 VARIABLE STRING 38 ALLOT
and then the commands PUTSTR and PRINTSTR will work as indicated.
On the next page, INSTR uses EXPECT for an input string (when it’s printed by PRINTSTR, your FORTH may then put three hearts at the end). The definition of INPUT (page 72) should be replaced with:
: INPUT CR ." ?" QUERY 1 WORD HERE NUMBER DROP ;
In this connection Haydon, in regard to CONVERT, notes: “This ideogram replaces the now obsolete (NUMBER) in the older fig-FORTH. As you might expect, no error message is given if the numeric text converts to a number larger than 32 bits. Any higher bits are lost.”
Number topics, including double and mixed precision arithmetic, as well as formatted number printing, are covered in chapter eight.
FORTH on the Atari will recognize a double number if it contains a decimal point, either embedded or at the end. This convention differs from that used in Leo Brodie’s book Starting Forth, which, in accordance with polyFORTH usage, looks for (page 164) one of these five punctuation characters (: , - . /) to place the entered value on the stack as a double-length integer. Winfield’s second footnote (page 75) makes an allusion to this fact.
Once again, you’ll need (page 76) to define:
: DNEGATE DMINUS ;
The word U/MOD, which in FORTH-79 divides a double number by a single number to leave a single precision remainder and quotient, should be redefined as:
: U/MOD U/ ;
and furthermore, R@, which copies a number n off the return stack onto the parameter stack, should be redefined as:
: R@ R ;
Formatted number output (page 80) uses a redefinition of .$ to print a string, which you may want to call .$$ instead; note that the date 310585. will print as /31/05/85. The definition of D- in fig-FORTH is:
: D- DMINUS D+ ;
since D+ exists in fig-FORTH. You will need:
: 2DROP DROP DROP ;
before the end of this chapter.
FORTH stands alone in its ability to define new structures within the language—the topic of the last tutorial chapter, chapter nine. The difference between human languages and computer languages is that all human languages are extensible, and computer languages are not—except FORTH.
Because Winfield defines ARRAY and X and TEST multiple times, you may prefer to number them sequentially as ARRAY1, ARRAY2,…to avoid possible confusion over error messages or storage locations in memory. You will need to define the first array as:
: ARRAY1 <BUILDS 2 * ALLOT DOES> SWAP 2 * + ;
and <BUILDS replaces CREATE (page 88) in his definition of new_defining_word. Here are Atari replacements for his defining word definitions:
: CONSTANT CREATE SMUDGE , ;CODE : VARIABLE CONSTANT ;CODE ( There is no FORTH-79 ( equivalent for ;CODE ) : CVARIABLE <BUILDS , DOES> ; : CCONSTANT <BUILDS C, DOES> C@ ; : 2VARIABLE <BUILDS 4 ALLOT DOES> ; : 2@ DUP 2+ @ SWAP @ ; : 2, HERE 2 ! 4 ALLOT ; : 2CONSTANT <BUILDS 2, DOES> 2@ ;
Here are fig-FORTH versions of the double number fetch and store operations (page 89):
: 2@ DUP 2+ @ SWAP @ ; : 2! SWAP OVER ! 2+ ! ;
Once again, replace CREATE with <BUILDS (pages 89 and 90). You’ll want to define an array called READINGS with ten elements as follows, to be used later (page 93):
10 ARRAY# ( array name ) READINGS 9 0 READINGS ! 8 1 READINGS ! 7 2 READINGS ! 6 3 READINGS ! 5 4 READINGS ! 4 5 READINGS ! 3 6 READINGS ! 2 7 READINGS ! 1 8 READINGS ! 0 9 READINGS !
Although WORD became WORD HERE earlier (see page 69), the definitions of INPUTS and PUT$ will work as shown.
The CONTINUE? routine to test for continue yes/no can be checked with this definition:
: YESNO 5 0 DO CR CONTINUE? CR LOOP ;
The definition of AVERAGE (page 93) should be changed to 1 0 DO, because you will be using 0-based indexing.
When you CREATE NULL (page 95), you can look at it in memory with HERE 6 - 4 TYPE where the inverse L signals the end of the name field. You can examine the code in memory for XSQ (page 96) with ' XSQ . (tic XSQ dot) to find the starting address and then define:
: HEXDUMP ( startadr endadr--) HEX DO I C@ . SPACE LOOP DECIMAL ;
and then execute ' XSQ 10 - 50 TYPE. The word FIND (page 97) should be defined as:
: FIND -FIND IF DROP ELSE 0 THEN ;
and EXECUTE in fig-FORTH should be one of the following:
: EXECUTE CFA EXECUTE ; or : EXECUTE 2 - EXECUTE ;
In the definition : TEST 4+ MODE? (page 98), omit the final semicolon before hitting RETURN.
The table of code field addresses (page 99) can be executed with this amended (fig-FORTH) version of Winfield’s code. Note that the example words each have five (5) characters, including blanks.
0 VARIABLE VECTORS ] ZERO ONE TWO [ : GOVECTOR 18 * ' VECTORS + 58 - CFA EXECUTE ;
The definition in the fig-FORTH Installation Manual of LITERAL (if compiling, create literal) is:
: LITERAL STATE @ IF COMPILE LIT , ENDIF ;
in which LIT is a primitive to push the following literal to the stack.
The last example in the chapter will wrok with this code:
: CATS ." cats " ; : .PFA [COMPILE] ' CFA EXECUTE ; .PFA CATS cats ok
Here are some final fig-FORTH definitions to make the list complete:
: EXIT R> DROP ; : NOT 0= ; : SAVE-BUFFERS FLUSH ; : D< D- SWAP DROP 0< ; : U< 0 SWAP 0 D< ; : U. 0 D. ; : DEPTH ( Team Atari Forth ) 234 SP@ - 2 / ;
Winfield concludes his book with two application programs: a largely mathematical calendar program and a video game that relies heavily on high speed graphics and, as he notes, is implementation dependent; to make it work on the Atari requires numerous changes—more than space allows.
The calendar program will work on the Atari almost as presented in the book, with the following minor exceptions.
All variables should be defined as 0 VARIABLE instead of VARIABLE.
There is a typographical error in his definition of JAN1ST, which returns the day of the week for the first of January in any given year. The error occurs on page 107 (Line 12 of Screen 100) where the sequence 2 B @ * - should read 2 A @ * - instead. The previous description that appears on page 104, however, is correct.
You will need to change CREATE DPMTABLE 31 C, (Screen 102 on page 108) to 0 VARIABLE - 2 ALLOT 31 C, because of the differences between CREATE in the two versions of FORTH.
Winfield uses an error message followed by the word ABORT in two places (Screen 102, Line 15 and Screen 105, Line 4), which you may want to change to QUIT. The word ABORT is the FORTH word for a warm start (in contrast to COLD in fig-FORTH for a cold start, which resets the dictionary pointer), which resets the data and return stack pointers and prints the fig-FORTH message. QUIT, on the other hand, merely brings you back to the text interpreter. QUIT is the endless loop which keeps invoking INTERPRET (the text or outer interpreter) and is the real operating system in FORTH.
Throughout the book, Winfield uses lower case letters to define new words and follows this practice in his concluding examples. This distinction makes it easy for the reader to differentiate his additions from the FORTH standard vocabulary. Lower case for new words will work in FORTH just as well as upper case, but the two are not interchangeable. Your FORTH will not recognize a name defined in lower case if you happen to type it in lower case. If you think this will impose an added mental burden, you may want to stick to upper case throughout.
The calendar program will give the day of the week for any date, or print a monthly or yearly calendar.
Here are answers to some of the questions your students are likely to ask:
What is the difference between fig-FORTH and FORTH-79?
Always remember that FORTH-79 is a subset of fig-FORTH, inasmuch as fig-FORTH includes nearly 300 core words, but FORTH-79 only has 121. “This does not mean that FORTH-79 is less useful or powerful than fig-FORTH,” according to C. Kevin McCabe (Forth Fundamentals) in his new book.
“It is mainly a difference in philosophy. Fig-FORTH includes many words for stack initialization, text parsing and other system functions. FORTH-79 does not include many system-level words, since there is little need for words to alter the text interpreter or change the manner of stack initialization. As a result, FORTH-79 is easier for a novice or end-user to comprehend, but less useful for system-level programming…
“Fig-FORTH does not restrict user access to any part of the dictionary. All fields may be inspected and (with necessary precautions) may be changed at will. FORTH-79 applications that access the dictionary are allowed to address only the parameter fields of variables, constants, and words compiled by user-created defining words, and dictionary space that has been left by ALLOT; all other parameter fields, and the name, code, and link fields of all words, must not be accessed…
“In general, many of the ‘bare bones’ of FORTH, such as stack pointer values and initializers, are hidden and inaccessible to the user of FORTH-79. The philosophy of FORTH-79 is to provide all necessary high level operations, such as DEPTH, but not some of the more fundamental operations used primarily to construct the equivalent fig-FORTH operations, this makes FORTH-79 less complex and more oriented to the novice programmer or end-user, while fig-FORTH is more useful to the experienced or system-level programmer.”
Where can we find detailed instructions to convert a fig-FORTH system to the FORTH-79 standard?
A 22-page booklet FORTH-79 Standard Conversion ($10.00 from Mountain View Press, P. O. Box 4656, Mountain View, CA 94040) has complete colon definitions and assembler code listings for several types of computers.
Why was this hook chosen instead of Starting Forth by Leo Brodie?
Brodie’s book is oriented toward polyFORTH which (1) is intended for professionals, and (2) features multitasking and multiprocessing, which do not exist on FORTHs for the Atari (because they are specifically designed for weekend hackers like you and me). The differences between fig-FORTH and FORTH-79 can be covered in a few pages, as you have seen; the polyFORTH differences would require almost a whole book.
What has been covered in this lecture?
Winfield’s book is complete only in the limited sense that he covers the complete FORTH-79 standard vocabulary, but omits the extension double number word set. Winfield actually covers the basics of the program development layer.
What has not been covered?
Winfield omits the other three FORTH layers such as applications, the operating system and the assembler, as well as floating point routines for program development.
What is the vocabulary of the extension double number word set?
It consists of: D- D0= D= DMAX DMIN DU< 2CONSTANT 2VARIABLE D. D.R 2! 2@ 2DROP 2DUP 2OVER 2ROT and 2SWAP.
What is the difference between FORTH-79 and the new FORTH-83 standard?
Eleven words have been renamed or removed, and twenty new words have been added. McCabe’s article in the August, 1984 Byte has the details (but no fig-FORTH colon definitions).
What have we accomplished today?
We now have the best of three worlds: system-level fig-FORTH, plus the popular contemporary FORTH-79, plus access to Atari’s arcade-type graphics.
Where can we go from here?
A 250-page book by Leo Scanlon on Forth Programming covers FORTH-79 in greater detail and includes the double number word set, with their colon definitions as well as several applications, such as use of sines and cosines, bubble and insertion sorts, and how to create a list of phone numbers.
Is it all right to reproduce the detachable FORTH-79 handy reference card?
Forth Interest Group publications have been placed in the public domain to insure the widest possible dissemination of the language, and may be freely reproduced.
How did you prepare for this lecture?
On a blank disk, I copied the two screens of error messages for my fig-FORTH. Then I copied all the statements from the book, chapter by chapter, with the necessary additions to convert fig-FORTH to FORTH-79. Each screen ended with ;S so they could be loaded one at a time. My friendly assistant here at the console called up the screens on request, and I just followed Alan Winfield’s well-written script, page by page.
Donald Forbes, Chartered Financial Analyst, is a computer systems analyst with a leading worldwide bank. He has been involved with large mainframes since 1958. His current interest is converting IBM, Wang and Atari microcomputers into FORTH virtual machines. As a hobby, he’s writing a book on the mathematics of mathematics. He lives in New Jersey with his wife Judy and their children.