F L I P T U T O R I A L
by James Allan Rhodes, Senior Digital Electronics Student
West Virginia State University Community and Technical College
FLIP was written by:
James Allan Rhodes, senior student Docs and word parser
Alex Raphael, senior student Graphics pack and editor (editor for version 2.8 only)
Davey Kensington, junior student Math pack
Mr. WS Walker, professor Overall guru
The reasons for writing this language are as follows:
· for credit in Digital Electronics and Robotics Class
· to prove to ourselves that we could do it
· as a learning tool to investigate how interpreters are written and
· to have a programming system to meet the following criteria:
a. An interactive interpreter type language
b. Full floating point math capability (as well as integer math)
c. To be able to control the three Jateck robots which were donated to the school by a local company (programmed via the 8086 ports)
d. To be able to run on any Intel based computer including the original 8086 and 8088 machines
e. To have graphics capability for plotting data
We decided on a class project to write a Forth-like language. It is written in 100% 8086 assembler using MASM.
Standard forth is not an interpretive language but FLIP is a true interpreter, not a compiler. Certain forth words are not included: those that apply to a compiler environment. Also, FLIP is heavy on math but very light on text handling capabilities. Text manipulations apply only to a special pad area and not to all of memory.
To run FLIP, just type “FLIP” at the DOS command line or in a DOS window under Windows 3.x, 9x, XP. You may rename the interpreter to any filename you wish.
There is only one command line option: FLIP FILENAME. This command line will cause the forth program sequence called filename.4 to be loaded and parsed. You may create a batch file, say "MYROBOT.BAT", with the text "FLIP MYFILE" contained therein. Then, by simply typing "MYROBOT" at the DOS prompt, the FLIP interpreter will load and parse the file (execute the words.) When the parser encounters a BYE command, then the interpreter will exit back to DOS. Thus, stand-alone programs can be built.
FLIP allows program sequences to be written by any standard ASCII text editor provided the line length is kept to 80 characters or less. (In graphics mode, 64 chars per line are recommended.) FLIP starts up with a yellow on blue screen in a DOS Window with "FLIP version n.n" printed across the top. The interactive prompt is a small triangle, which we shall designate as a "►" in this document.
FLIP is case insensitive. The word commands can be in either uppercase or lowercase or any combination.
It has two screen modes of operation: text mode and graphics mode. In text mode, the screen is 25 rows by 80 columns. You cannot do pixel graphics in text mode (can’t plot a pixel for instance.) In graphics mode, the screen is 640 x 350 pixels and 16 colors are available (EGA mode 10h.) You may plot pixels, draw lines and circles using internal FLIP words. Also, a special font is available that allows text in 35 rows of 64 characters.
To follow along with this tutorial, bring up the FLIP interpreter in a DOS window alongside this document. (Double clicking on the file FLIP.exe under any version of Windows will open up a DOS Window with the interpreter running in it.)
1. NUMBERS AND MATH
To enter a number on the stack, just type it in and hit enter. Integer numbers, those without a decimal point, go onto the integer stack while floating point numbers, those with a decimal point or an exponent, go onto the float stack.
►123 <enter>
puts the integer number 123 on top of the integer stack (TOIS). Both the integer and the float stacks hold 10 entries. Entries beyond that will cause a stack overflow error.
To see the number on the integer stack (from here on we won't show the <enter> key; just assume that anything entered after the prompt is always terminated with the enter key):
►.s
123
►
'.S’ is a predefined word that means "display the integer stack." Note that the stack at this point contains only one number, the one we just entered. Now type in this:
►456 789 .s
123 456 789
►
We entered two more entries on the integer stack and then displayed the stack contents. As many FLIP words as you want -- as long as they will fit in 80 characters -- can be entered at the prompt; just remember to separate them by at least one space. (Later, we will see how to exceed the 80 character limit.) Now type:
►.
789►
The DOT is the FLIP word to print the TOIS. This also removes the number from the stack. Printing the integer does not automatically issue a new line. You must do that yourself. Try this:
►. cr
456
►
The next integer on the stack is printed, this time with a new line at the end -- which is what 'CR' does. There is still one more piece of data on the integer stack, 123. So let's do a little integer math with it:
►dup + . cr
246
►
'DUP' means to duplicate the stack. '+' says “add the two values on top of the stack and put the answer as the new top of stack.” That is why 246 was printed. These operations have cleared the stack, so if you enter '.s' now you will see this:
►.s
-- integer stack empty --
►
If you haven't guessed already, you can add, subtract, multiply, or divide the two top values on the stack by using the '+', '-', '*', or '/' words. Beware: this is 16 bit integer arithmetic with a valid range of -32768 to +32767. Try this:
► 32766 3 + . cr
-32767
►
The answer exceeded the amount allowed for a positive integer, so it "wrapped around" to a negative one. This is called modular arithmetic – like doing calculations with time. 11 o’clock plus 2 hours gives 1 o’clock, not 13 o’clock.
Errors do not clear the stacks. You can clear them by one of the following words: 'EMPTY', which clears only the integer stack; 'FEMPTY', which clears only the float stack; or 'CLEAN', which clears both the integer and the float stacks. The word 'COLD' which cold starts the interpreter will also clear the stacks, but it also clears word definitions, program storage, run stacks, and the pads as well.
OK, let's do some floating point manipulation:
►1.234 5.678 9.012 .f
1.2340000 5.6780000 9.0120000
►
We entered three numbers on the float stack (because the numbers have a decimal point) and then displayed the contents of the stack using the '.F’ word. We can drop and print the top entry (followed by a new line) by:
►f. cr
9.0120000
►
We can multiply the remaining two float stack entries and print the answer this way:
►f* f. cr
7.0066520
►
The float stack is now empty:
►.f
-- float stack empty--
►
What if you want to print more than just 7 decimal places? Do this:
►1. 3. f/ 59 fplaces f. cr
0.333333333
►
Nine decimals of the fraction 1/3 were printed out. In general, when a two digit number is placed on the integer stack followed by an 'FPLACES' word, then the format of all subsequent 'F.'s will be to the number of decimal places given by the second digit. The first digit of the format number sets the number of places to the left of the decimal. Let's do it again but with a different format number:
►1. 3. f/ 12 fplaces f. cr
0.33
►
Note that 'FPLACES' also "eats" the formatting numbers off the integer stack. The number formatting also applies when displaying the stack with '.F'.
You may also use the exponential form of floating point numbers. Try this:
►1.23456e4 e. cr
1.234560000E+04
►
And try this: (make sure you type 15 zeros after the 1 and then type a decimal point)
►1000000000000000. e. cr
1.000000000E+15
►
You can add, subtract, multiply, divide, take the absolute value, do exponentiation, logs, sines, cosines, and many other operations on floats. For example, 'FSIN' will take the trigonometric sine of the TOFS (top of float stack) and replace it with the result. The angle must be in radians.
►30. pi f* 180. f/ fsin 36 fplaces f. cr
0.500000
►
This sequence took the angle in degrees (30.), converted it to radians by multiplying it by pi and then dividing it by 180., took the trigonometric sine, set the format to 3.6 places, printed the result and issued a new line.
2. WORD DEFINITIONS and TEXT MANIPULATION
Let's say we wanted the conversion from degrees to radians and the sine function of the result to be done automatically. That is, make a routine that will take a value in degrees and replace it with its sine. To do this, we must define a new word. This is done with the COLON and SEMI-COLON. The colon begins the definition, followed by the word's name, followed by any other word commands, and ends with a semi-colon. The word name can be any sequence of characters. Even “@#$%^&” could be defined as a word. Note that (in version 3.1), the internal words OVERRIDE the user defined words. So if you try to create a new word called DUP, for instance, the user word will never be able to be executed.
►: sindeg pi f* 180. f/ fsin ;
►
Now check to see if the word has been defined. Type the word ‘WORDS':
►words
root words:
.S . + - * / D* D/ CR .F F. F+ F- F/ DUP FDUP DROP FDROP SWAP FSWAP
“ TYPE LEN CLS \ ( FVARIABLE VARIABLE F! F@ ! @ F? ? IF JUMP 0< 0= 0>
F0< F0= F0> < = > F< F= F> F0~ NOT AND OR XOR ROT FROT PICK FPICK : ;
STOP SKIP DO LOOP +LOOP I J K UNDO BEGIN AGAIN UNBEGIN ACCEPT EXECUTE
EMIT SPACE SPACES 1+ 1- INC DEC FINT F>N N>F KEY FSQRT F10^X FE^X FLOG10
FLN FTAN PI FD2R FR2D FATAN FSIN FCOS ABS FABS NEG FNEG FEMPTY EMPTY CLEAN
DEPTH FDEPTH PLACES FPLACES FGCOLOR BGCOLOR INCOLOR COLD LIST LOAD +LOAD
WORDS H. AT-XY TRUE FALSE BYE MEM ALLOT FALLOT PUSH POP FPUSH FPOP EPSILON!
EPSILON@ FCONSTANT CONSTANT VLIST , F, TEE-ON TEE-OFF GRAPHICS PLOT TEXT
FORGET FATAN2 MOD FACOS FASIN FPWR FCUBRT FSINH FCOSH FTANH FATANH LINE
EXIT NUMBER FNUMBER STRING FSTRING CIRCLE PAD! PAD@ FREE PAD C! C@ ‘ DROP2
FDROP2 +SPACES SLICE CONCAT UPPER LOWER BLACK DARKBLUE DARKGREEN DARKCYAN
DARKRED DARKMAGENTA BROWN GREY DARKGREY BLUE GREEN CYAN RED MAGENTA YELLOW
OPEN-FILE CLOSE-FILE WRITE-FILE READ-FILE TAB LOCATE READ WRITE APPEND
TRACE-ON TRACE-OFF E. EPLACES ESTRING .P G. L. FONT XORGR-ON XORGR-OFF
SYSTEM W@ W! REPARSE >> << ERROR-ON ERROR-OFF CLEAR-MEM FREE
user words:
SINDEG
►
All the words that were printed up to line that says "user words" are internally defined FLIP words. They are always there (even after a 'COLD'). Below the line that says "user words" are the words you have defined. SINDEG was just defined, so it has been entered into the word definition table (we call it the dictionary.) Now do this:
►60. sindeg f. cr
0.866025
►
The word 'sindeg' took the value of the angle (in degrees) off the TOFS (60. in this case), converted it to radians, and then replaced it with the sine value, which is what your new word was designed to do. The rest of the line printed the value and issued a carriage return.
To see the user defined words, use the LIST command:
►list
: sindeg pi f* 180. f/ fsin ;
►
To remove a word, type:
►forget
►list
(nothing printed)
►words
....
....
....
user words:
-- none --
►
Note that 'sindeg' is no longer defined. ‘FORGET’ only forgets the last word defined. You may; however, issue as many forgets as you want to clear out other words. For example, three forgets will remove the last three words defined.
Occasionally you may want to display some text on the screen. Here is the way to do it:
►" Hello, world!" len type cr
Hello, world!
►
The " is the quote word (don't forget the space after it!) Everything after the <"-space> up to the next <"-space> is put into a text pad. The word 'LEN' sets the TOIS (top of integer stack) to the count of characters in the pad. The 'TYPE' word displays n characters from the pad, n being the value on the TOIS. Typing the pad does not destroy it (but it does eat up the count.) So to display it again, just do another 'LEN TYPE CR’ sequence.
Try this:
►5 type cr
Hello
►
And this:
►-6 type cr
world!
►
Hence, a positive count takes that many characters from the left end of the pad while a negative count takes that many characters from the right end of the pad. '0 TYPE' displays nothing. Any count (positive or negative) greater than or equal to the actual count of characters in the pad will display the whole pad. To print a section in the middle of the pad, say from the second to the fifth character:
►2 5 slice len type
ello
►
This sequence has replaced the pad with a slice of the old contents, in this case from character 2 to character 5, and then printed it.
►upper len type cr
ELLO
►
This sequence converted the pad to upper case and displayed it. ‘LOWER’ also works as you might expect.
Say you want to input a number on the fly. The 'ACCEPT' word will do this. First, it waits for input then it puts everything you type into the pad:
►accept cr len type cr
123.456 ←you type this as a response to 'accept'
123.456
►
But hold on, this put text into the pad, not a number on the stack. To accomplish the task of making it a number, use the execute command.
►execute f. cr
123.456000
►
Execute takes the pad as a sequence and executes it just as if it was the prompt line. Since the pad contains the text 123.456, that sequence when entered at the prompt puts the number on the TOFS.
Note that if the pad contained the text '123 456 3.142 987 2.718' then an 'execute' will put 123 and 456 on the integer stack and 3.142 and 2.718 on the float stack. This will prove very useful when you do file reads (which are always text) and need to make them numbers.
You can even put word definitions in the pad:
►" : degtorad pi f* 180. f/ ; "
►
Do a 'LEN TYPE CR’ to verify what's in the pad and then do an 'EXECUTE'. The word will be defined and should now show up when you do a 'WORDS' or 'LIST'. So, try a sequence that uses the word and see if it works:
►45. degtorad fcos e. cr
7.071067812E-01
►
The word definition is still in the pad (as well as being in the dictionary), so we can save it to a special place:
►1 pad!
►
This sequence saves the pad in a pad save area; there are three of them -- we just used the first one. 'PAD!' means "save the pad" and the pad save area number is on the TOIS. Now destroy the pad by the following sequence:
►" " len type cr
►forget
►
The definition is now gone from the word table and program sequence memory. Verify that with 'WORDS' and ‘LIST’.
Ok, we'll now bring back the pad from its saved area and then redefine the word it contains:
►1 pad@ execute
►
'PAD@' means pad fetch and again the save area number to fetch from is on the TOIS. If you do 'WORDS' again then you'll find that 'degtorad' is back! Prove it by doing this sequence:
►45. degtorad ftan f. cr
1.000000
►
It computes the tangent of 45 degrees which is exactly 1, and used the 'degtorad' word that was "resurrected."
By the way, if you do 'EXECUTE' again, you'll see this:
►execute
DEGTORAD word already defined
This is not an error, but a warning. A word definition can only be executed once. If you need to redefine it, use ‘forget’ then recreate it. (Warnings are printed in yellow, errors in red, and information in green.)
3. VARIABLES and ARRAYS
You can create variables and then store and fetch numbers from them. First, do a cold start to reset everything.
►cold
+ + + F L I P version 3.1 + + +
►
You have just reset the FLIP interpreter. Now type in:
►variable x 123 x !
►
This creates a variable called x. Then 123 is put on the TOIS. Next the 'x' causes the current ADDRESS (not value) of x to be put on the TOIS, pushing the value 123 down one level on the stack. The '!' is the integer store word. It says "using the address on top of the integer stack store the value into the location that is next on the stack." So 123 is stored into wherever x is located. And just where is it located? In standard forth that can be just about anywhere in memory. In FLIP, all variables are stored in special save areas that are indexed. The first integer variable created has index 1, the next has index 2, and so forth. So that:
►variable y
►
creates y at index 2 -- x was created at index 1. When the variable name is typed in, it is the index value that gets placed on the top of the integer stack.
►456 y !
►
stores the value 456 into integer indexed at 2 whose name is called 'y'.
To get the values back, just do this:
►x @ . cr
123
►
The '@' is the fetch word. Since 'x' puts the index value for x on the integer stack (1 in this case), the '@' says "go to that address, get the value, and put it on top of the integer stack." Then the DOT prints the value and the 'CR' issues a new line.
Note that '!' consumes the address and the value from the stack while '@' consumes the address but leaves a value on the stack.
Say you want to create an array. To do this we will use the ',' word which means "store the value on the TOIS to the address (index value) last created with a variable declaration and then increase the index by one."
►variable myarray 9 , 8 , 7 , 6 , 5 , ← the comma causes the 9 to be stored in myarray (this time, index value 1)
then increments to the next index value (which will be 2).
►myarray 2 + @ . cr ← myarray variable name cause its index value (1) to be put on TOIS. 2 is
added to it, thus referencing index 3.
7 ← the third element is fetched and printed resulting in 7 being displayed.
►
An array of 5 values has been created. The name of the array is myarray. To fetch the third value in the array, 2 is added to the address (index) before fetching and printing. In general, for an array of data, the i'th data element of the array is obtained by adding i-1 to the index value before fetching.
As long as no other variable declarations intervene, more data can be appended to the array by entering the data on the TOIS and then using the ',' word to store it.
A sequence that would print the third element of myarray:
►3 ? ← the programmer knows that myarray is at index 1 so the third element is at index 3. The ‘?’ is a
short cut for fetch and print.
7
►
This is perfectly OK, but it requires the programmer to keep track of memory locations. If you have dozens of variables, some perhaps an array, keeping track of all the locations may be tedious.
Sometimes you may want to define a constant whose value will stay the same all the time. You can do it this way:
►constant hpixel 600
►hpixel . cr
600
►
For constants, fetching is not needed. The name 'hpixel' retrieves the value and not the address. Standard forth does the same.
The rules of storing and fetching also apply to floats, except the words 'FVARIABLE', 'F!', 'F@', and 'F,' and 'FCONSTANT' are used.
►fvariable p2 6.283185 p2 f!
►p2 f@ f. cr
6.283185
►
p2 is declared a float variable and 6.283185 is stored to it. If this is the first float variable defined, its index value would be 1. Then p2 is fetched and printed. The second line could also be
►p2 f? cr.
Float constants work like integer constants:
►fconstant sq2 1.414213
►sq2 f. cr
1.414213
To see all the variables that have been created, issue the word 'VLIST':
►vlist
variables:
X Y MYARRAY
fvariables:
P2
constants:
HPIXEL
fconstants:
SQ2
►
If you want to save space in the variable storage area for array values but you wish to assign the values later, then use 'ALLOT' for integers and 'FALLOT' for floats. For example:
►variable hold 10 allot
►
This sequence sets up room for 11 integers variables: hold, hold+1, ... hold+10.
Any future variable declarations will come at the end of this space. Later, you can do something like this:
►3416 hold 5 + !
►
which stores the number 3416 into the 6th element of the array.
Note: if you store data beyond what was allotted for the array, you may clobber some other variable. FLIP doesn't care; it will let you do this. That could actually be useful. If array 'myarray' has been allotted to 10 (making 11 elements) and then the variable 'q' is defined right after that; it will be stored at index position 12. Hence, the sequence 'myarray 11 + 666 !' will store 666 into 'q'! It could likewise be fetched as the 12th element of 'myarray'.
Fallot will work the same way on float variables.
Vlist shows space saved using allot or fallot as sequence of dots:
►vlist
variables:
X Y MYARRAY HOLD . . . . . . . . . .
fvariables:
P2
constants:
HPIXEL
fconstants:
SQ2
►
Each dot after HOLD represents a memory location reserved. Note that since allot wasn't used with MYARRAY, then there was no reserved space for it, Hence,
►myarray 6 + @ . cr
3416
►
which is the same element as hold 5 +.
Here are a few useful words. Let's say you have a number on top of the integer stack -- it could be the result of some calculation. You would like to make it into a string and append it to the pad (we will manually put a number on the TOIS, but pretend it was computed):
► " " ←this clears the pad
►2468 string len type cr ←pretend the 2468 was calculated then convert it to a string and append it to the pad. Then print the pad.
2468
►
‘FSTRING’ and ‘ESTRING’ are used for conversion to float strings (these examples use the same pad results from above):
►12345.67 fstring len type cr
2468 12345.6700000
►
► 6.23E+23 estring len type cr
2468 12345.6700000 6.230000000E+23
►
FSTRING is like F., it converts floats to fixed point, while ESTRING and E. convert floats to exponential form.
4. TEXT COLOR and DATE/TIME FUNCTIONS
Anytime you want to change the text color, just enter the color name followed by the 'FGCOLOR' word:
►" This is blue" blue fgcolor len type cr
This is blue
Obviously, the above line is printed in blue. The background color can be changed this way:
►" This is green on black" black bgcolor cls green fgcolor len type cr
(the screen clears to black)
This is green on black
►
All text colors stay in effect until changed again or are set to the default yellow on blue when a 'COLD' (or a 'LOAD') is executed.
The word 'DATE' and 'TIME' puts the date or time on the stack:
►date . . .
12 10 1987 ←month, day, year
►time . . .
18 14 6 ←hour, mins, secs
Note that time is 24 hour based.
5. MORE STACK MANIPULATIONS and COMPARISONS
Some common integer stack manipulations are ‘DUP’ (duplicate), ‘DROP’, ‘SWAP’ and ‘ROT’ (rotate.) Each of these also has a counter part for the float stack: ‘FDUP’, ‘FDROP’, ‘FSAWP’, and ‘FROT’. ‘DUP’ duplicates the stack. For example, to square a number on top of the integer stack:
►123 dup * . cr
15129
►
Here is the same example in float math:
►123.456 fdup f* f. cr
15241.3839360
►
‘DROP’ or ’FDROP’ simply drops a value from the integer or float stack:
► 1 2 3 4 drop .s
1 2 3
►
► 1. 2e4 .333 .4e5 fdrop .f
1.00000000 20000.0000000 0.3330000
►
‘SWAP’ or ‘FSWAP’ exchanges the two top values on the integer or float stack, respectively:
► swap .s
1 3 2
►
►fswap .f
1.0000000 0.3330000 20000.0000000
►
‘ROT’ or ‘FROT’ works the same as ‘SWAP’ if there are only two entries on the stack. If three or more entries exist on the stack, then the third entry is moved to the first (assume the stack is the same it was after the last example):
►rot .s
3 2 1
►
►frot .f
0.3330000 2000.0000000 1.00000000
►
Values on the stack can be tested or compared. To test for integer zero, use '0='. The value on top of the integer stack is tested, dropped, and either a true (-1) or a false (0) is placed back on TOIS depending on the results of the test. Using the stack from the last example:
►0= .s
3 2 0
►
Since a '1' was on top of the stack, the test for zero failed. It was replaced with a false, which is 0. This example does the same thing with the float stack:
►f0= .f .s
0.3330000 2000.0000000
3 2 0 0
►
The test for float equal to zero failed since the TOFS was a 1.0000000. This float value was dropped. A false, which is an integer zero was placed on top of the integer stack.
You may test for integer and float equal to zero, less than zero (negative) and greater than zero (positive). These are all test on a single value.
You may also do comparison tests. The second stack entry may be compared to the top stack entry using ‘=’, “<’, and ‘>’ for integers or ‘f=’, ‘f<’, and ‘f>’ for floats. But first, we will clean the stacks:
►clean
►
In this example, we introduce two new words. First is the word ‘RANDOM’ which places a random positive integer on top of the integer stack. Next, the ‘L.’ word prints a logical integer; that is, it prints a ‘True’ if the value on TOIS is -1 or ‘False’ if the value on TOIS is 0.
►random 16384 > l. cr ←random puts a random number on top of the stack. Then 16384 is put on top next, pushing
the random number down one. The ‘>’ tests to see if the random number is greater than
16384. The test drops the two values and puts a true or a false (-1 or 0) on top of the stack.
Finally, the l. prints the logical result.
True ←The logical result is printed; of course, if you try this, it may be false since the number
being compare to 16384 is random.
►
6. DECISION MAKING
The ‘IF’ statement allows the program sequence to make decisions. It looks at the top of the integer stack and assumes a logical integer (true or false, -1 or 0) is there. If the TOIS is true, the rest of the line is parsed; if the TOIS is false the rest of the line is not parsed.
►random dup 16384 > if . ←prints the random number only if it is greater than 16384
17546
►
You may have to run this sequence several times to get a random value that will print. Note, however, that the integer stack will be getting larger each time the ‘IF’ fails. Why? Because the ‘DUP’ is there to put an extra copy on the integer stack for printing (the ‘>’ comparison “eats” one copy of the random number as well as the 16384). So if the ‘IF’ fails, the duplicate value stays on the stack. To see how we can fix this, we introduce the ‘JUMP’ word. Jump performs the following: first, it grabs the next token after the jump word. (A ‘token’ is a string delimited by spaces on the sequence line. In the above example, ‘random’, ‘dup’, ‘16384’, ‘>’, ‘if’ and ‘.’ are all tokens.) Next, it assumes the token is a tag name to jump to and searches the entire program sequence space as well as the prompt line for a similar tag. Tags are placed in the sequence by prepending a ‘~’ to the tag name. Hence, ‘~ABCD’ is a tag and ‘JUMP ABCD’ will transfer parsing to a point just following the tag. So try this:
►0
►~here 1 + dup dup . 10 < if jump here
1 2 3 4 5 6 7 8 9 10►
Let’s analyze carefully what happens in this sequence. It contains some important programming concepts. Essentially, the sequence counts to 10 displaying each integer as it counts.
The first line puts an integer 0 on the stack. The next line starts off with a tag ‘~here’. Tags are completely ignored by the parser except when a jump is encountered. Just after the ~here are the ‘1’ and ‘+’ plus words that adds 1 to the TOIS, making it 1. The two ‘dups’ make two copies of the 1 on the integer stack. The stack is now 1 1 1. The dot drops and prints one of the 1’s, leaving: 1 1. The next two words put a 10 on the stack and perform a less-than compare. Since 1 is less than 10 (the two values are consumed by the test), the stack now contains a 1 -1; where 1 is the count and the -1 is a ‘true’. The ‘if’ word looks at the true and decides to parse the rest of the line which says “jump to here”. Since the here tag is at the beginning of the sequence, it is parsed all over again. And since the ‘if’ “ate” the -1, the only thing left on the integer stack is the 1. It should be clear what happens this next time through the sequence: the 1 is increment, duped, printed, tested and since 2 is less than 10, the whole sequence goes again. Eventually, a 10 is printed, increment to 11, the test for the count < 10 fails, a false is put on the stack, the ‘if’ fails, the jump is not parsed, and the sequence ends and the prompt appears. Thus, all the integers from 1 to 10 are printed.
Returning to the random number test and print sequence, we need to drop the stack if the test fails to keep the stack “clean.” We will make the sequence into a word and learn how to define a multi-line sequence at the prompt. Remember that a new word definition is a ‘colon’ to start the word, a bunch of tokens, and a ‘semi-colon’ to end the word. What happens if the prompt line has the ‘colon’ to start the new word but no ‘semi-colon’ to end it? The interpreter will continue accepting input until it sees the semi-colon.
►: testran random dup 16384 > if . cr jump randisp ←a new word is being defined.
+► drop ~randisp ; ←since the semicolon was missing in the line above, you’re
prompted to continue. The semi-colon is typed on this line.
Also note that the prompt changed to a +► to signify more
input is needed to complete the word definition.
► testran ←test the new word
20331 ←in this case, the random number was greater than
16384 so it was printed (which drops the number)
► testran ←test it again. This time the random number is less than
16384, nothing is printed, but the number is still dropped
►
What is different this time is that if the test succeeds, the number is printed and the ‘drop’ is skipped. If the test fails the number is not printed but the ‘drop’ is executed. This means the stack is properly cleared of “junk.”
This program sequence can be shortened. The ‘EXIT’ word is essentially a jump to end of the word definition; that is, a jump to the semi-colon. So we can redefine the testran word as follows:
► forget ← first we forget the old definition of testran
► : testran random dup 16384 > if . cr exit ← redefine testran
+► drop ;
► 20 1 do testran loop ← do testran 20 times
17186 ← 14 times the test passed, 6 times it failed (your numbers will be different)
18557
22830
22151
23396
21509
16836
29782
24168
18635
20402
19166
18125
27032
►.s ← display the integer stack
-- integer stack empty -- ← there is no “junk” left on the stack
7. USING AN EDITOR TO CREATE PROGRAMS
Use Notepad to open up a text editing session. In DOS, use the EDIT FILENAME command. In the editor, enter the following program sequence:
: fibonacci ( print out the fibonacci series )
" The Fibonacci series is: " len type cr
1 dup . 2 dup .
dup rot + dup .
dup 20000 < if -2 skip
cr drop2
;
Save this program out as FIBO.4 (which should be the name of the file following the EDIT command if using the DOS editor). Open up FLIP in a DOS Window as before, if not already running. At the FLIP prompt, enter:
► load fibo
►
If you made no mistakes, a program sequence to compute the famous Fibonacci sequence is in memory. Type LIST to verify that the stuff you typed into the editor above is now in memory – what we call the program sequence space in FLIP.
For those not familiar with the Fibonacci sequence, it is a series of numbers starting with 1,2 and then each new number is the sum of the previous two. Study the sequence and see if you can figure out how it works. Realize that the ‘(-space’ word starts a comment and the ‘)’ ends it. Also note that in the second to last line, after the “if’ is a sequence ‘-2 skip’. What ‘SKIP” does is look at the TOIS and use that as an offset line counter for parsing to resume. A ‘0 skip’ is a no-operation since that is where parsing would continue anyway – on the next line. So a ‘-2 skip’ refers to the line 2 lines back from the next line. In the Fibonacci example that would be the line ‘dup rot + dup .’
A ‘+5 skip’ would skip to the fifth line following the current line. (The plus sign is optional for forward skips.) Note that using if you use skip word in a single prompt line sequence, the only thing that makes sense is ‘-1 skip’ which would cause the whole line to be parsed again.
Now type in at the prompt:
► Fibonacci
The Fibonacci series is:
1 2 3 5 8 13 21 34 55 89 144 233 377
610 987 1597 2584 4181 6765 10946 17711 28657
All the Fibonacci numbers up to the one just past 20,000 is printed. The next number in the sequence would exceed the maximum positive integer value of 32767. Let’s edit the program using our text editor to the following:
: fibonacci ( print out the fibonacci series )
" The Fibonacci series is: " len type cr
60 fplaces
1. fdup f. 2. fdup f.
fdup frot f+ fdup f.
fdup 2e5 f< if -2 skip
cr fdrop2
;
This program uses float math to compute the Fibonacci numbers up to 200,000.