diff options
Diffstat (limited to 'forth/pf_tut.md')
-rw-r--r-- | forth/pf_tut.md | 1345 |
1 files changed, 1345 insertions, 0 deletions
diff --git a/forth/pf_tut.md b/forth/pf_tut.md new file mode 100644 index 0000000..5b4d608 --- /dev/null +++ b/forth/pf_tut.md @@ -0,0 +1,1345 @@ +::::::::::::: {#container} +:::::: {#header} +::: {#leftheader} +[{width="200" height="100" +border="0"}](/) +::: + +::: {#rightheader} +::: + +::: {#midheader} +# SoftSynth + +## \... music and computers \... +::: + +\ +:::::: + +:::: {#leftside} +::: {#leftside_inner} +- [Home](/index.php) +- [Products](/products.php) +- [JSyn](/jsyn/index.php) +- [Syntona](/syntona/index.php) +- [pForth](/pforth/index.php) +- [Music](/music/index.php) +- [Info](/info/index.php) +- [News](/news/index.php) +- [Links](/links/index.php) +- [Contact Us](/contacts.php) +- [About Us](/aboutus.php) +::: +:::: + +:::: {#rightside} +::: {#rightside_inner} +### Projects + + --------------------------------------------------------------------------------------------------- + [JSyn](/jsyn/) - modular synthesis API for Java. + [JMSL](https://www.algomusic.com/jmsl/){target="_blank"} - Java Music Specification Language + [PortAudio](https://www.portaudio.com/){target="_blank"} - cross platform audio I/O API for \'C\' + --------------------------------------------------------------------------------------------------- +::: +:::: + +::: {#content} +[pForth](/pforth/index.php) + : [GitHub](https://github.com/philburk/pforth/) + \| [Tutorial]{.current_link} \| [Reference](/pforth/pf_ref.php) + \| [Links](/forthlinks.php) + +------------------------------------------------------------------------ + +# Forth Tutorial + +------------------------------------------------------------------------ + +Translations: +[Chinese](http://vision.twbbs.org/%7Eletoh/forth/pf_tuttw.html){target="_blank"} +by +[Letoh](http://vision.twbbs.org/%7Eletoh/blog/?page_id=169){target="_blank"} + +by [Phil Burk](http://www.softsynth.com/philburk.html) of +[SoftSynth.com](http://www.softsynth.com) + +## Table of Contents + +- [Forth Syntax](#Forth%20Syntax) +- [Stack Manipulation](#The%20Stack) +- [Arithmetic](#Arithmetic) +- [Defining a New Word](#Defining%20a%20New%20Word) +- [More Arithmetic](#More%20Arithmetic) + - [Arithmetic Overflow](#Arithmetic%20Overflow) + - [Convert Algebraic Expressions to + Forth](#Convert%20Algebraic%20Expressions%20to%20Forth) +- [Character Input and Output](#Character%20Input%20and%20Output) +- [Compiling from Files](#Compiling%20from%20Files) +- [Variables](#Variables) +- [Constants](#Constants) +- [Logical Operators](#Logical%20Operators) +- [Conditionals - IF ELSE THEN + CASE](#Conditionals%20-%20IF%20ELSE%20THEN%20CASE) +- [Loops](#Loops) +- [Text Input and Output](#Text%20Input%20and%20Output) +- [Changing Numeric Base](#Changing%20Numeric%20Base) +- [Answers to Problems](#Answers%20to%20Problems) + +The intent of this tutorial is to provide a series of experiments that +will introduce you to the major concepts of Forth. It is only a starting +point. Feel free to deviate from the sequences I provide. A free form +investigation that is based on your curiosity is probably the best way +to learn any language. Forth is especially well adapted to this type of +learning. + +This tutorial is written for the PForth implementation of the ANS Forth +standard. I have tried to restrict this tutorial to words that are part +of the ANS standard but some PForth specific words may have crept in. + +In the tutorials, I will print the things you need to type in upper +case, and indent them. You can enter them in upper or lower case. At the +end of each line, press the RETURN (or ENTER) key; this causes Forth to +interpret what you\'ve entered. + +## []{#Forth Syntax}Forth Syntax + +Forth has one of the simplest syntaxes of any computer language. The +syntax can be stated as follows, \"**Forth code is a bunch of words with +spaces between them.**\" This is even simpler than English! Each *word* +is equivalent to a function or subroutine in a language like \'C\'. They +are executed in the order they appear in the code. The following +statement, for example, could appear in a Forth program: + +- WAKE.UP EAT.BREAKFAST WORK EAT.DINNER PLAY SLEEP + +Notice that WAKE.UP has a dot between the WAKE and UP. The dot has no +particular meaning to the Forth compiler. I simply used a dot to connect +the two words together to make one word. Forth word names can have any +combination of letters, numbers, or punctuation. We will encounter words +with names like: + +- ." #S SWAP ! @ ACCEPT . * + +They are all called *words*. The word **\$%%-GL7OP** is a legal Forth +name, although not a very good one. It is up to the programmer to name +words in a sensible manner. + +Now it is time to run your Forth and begin experimenting. Please consult +the manual for your Forth for instructions on how to run it. + +## []{#The Stack}Stack Manipulation + +The Forth language is based on the concept of a *stack*. Imagine a stack +of blocks with numbers on them. You can add or remove numbers from the +top of the stack. You can also rearrange the order of the numbers. Forth +uses several stacks. The *DataStack* is the one used for passing data +between Forth words so we will concentrate our attention there. The +*Return Stack* is another Forth stack that is primarily for internal +system use. In this tutorial, when we refer to the \"stack,\" we will be +referring to the Data Stack. + +The stack is initially empty. To put some numbers on the stack, enter: + +- 23 7 9182 + +Let\'s now print the number on top of the stack using the Forth word \' +**.** \', which is pronounced \" dot \". This is a hard word to write +about in a manual because it is a single period. + +Enter: **. ** + +You should see the last number you entered, 9182 , printed. Forth has a +very handy word for showing you what\'s on the stack. It is **.S** , +which is pronounced \"dot S\". The name was constructed from \"dot\" for +print, and \"S\" for stack. (PForth will automatically print the stack +after every line if the TRACE-STACK variable is set to TRUE.) If you +enter: + +- .S + +you will see your numbers in a list. The number at the far right is the +one on top of the stack. + +You will notice that the 9182 is not on the stack. The word \' . \' +removes the number on top of the stack before printing it. In contrast, +\' .S \' leaves the stack untouched. + +We have a way of documenting the effect of words on the stack with a +*stack diagram*. A stack diagram is contained in parentheses. In Forth, +the parentheses indicate a comment. In the examples that follow, you do +not need to type in the comments. When you are programming, of course, +we encourage the use of comments and stack diagrams to make your code +more readable. In this manual, we often indicate stack diagrams in +**bold text** like the one that follows. Do not type these in. The stack +diagram for a word like \' . \' would be: + +**`. ( N -- , print number on top of stack )`** + +The symbols to the left of \-- describe the parameters that a word +expects to process. In this example, N stands for any integer number. To +the right of \--, up to the comma, is a description of the stack +parameters when the word is finished, in this case there are none +because \'dot\' \"eats\" the N that was passed in. (Note that the stack +descriptions are not necessary, but they are a great help when learning +other peoples programs.) + +The text following the comma is an English description of the word. You +will note that after the \-- , N is gone. You may be concerned about the +fact that there were other numbers on the stack, namely 23 and 7 . The +stack diagram, however, only describes the portion of the stack that is +affected by the word. For a more detailed description of the stack +diagrams, there is a special section on them in this manual right before +the main glossary section. + +Between examples, you will probably want to clear the stack. If you +enter **0SP**, pronounced \"zero S P\", then the stack will be cleared. + +Since the stack is central to Forth, it is important to be able to alter +the stack easily. Let\'s look at some more words that manipulate the +stack. Enter: + +- 0SP .S \ That's a 'zero' 0, not an 'oh' O. + 777 DUP .S + +You will notice that there are two copies of 777 on the stack. The word +**DUP** duplicates the top item on the stack. This is useful when you +want to use the number on top of the stack and still have a copy. The +stack diagram for DUP would be: + +**`DUP ( n -- n n , DUPlicate top of stack )`** + +Another useful word, is **SWAP**. Enter: + +- 0SP + 23 7 .S + SWAP .S + SWAP .S + +The stack diagram for SWAP would be: + +**`SWAP ( a b -- b a , swap top two items on stack )`** + +Now enter: + +- OVER .S + OVER .S + +The word **OVER** causes a copy of the second item on the stack to +leapfrog over the first. It\'s stack diagram would be: + +**`OVER ( a b -- a b a , copy second item on stack )`** + +Here is another commonly used Forth word: + +**`DROP ( a -- , remove item from the stack )`** + +Can you guess what we will see if we enter: + +- 0SP 11 22 .S + DROP .S + +Another handy word for manipulating the stack is **ROT**. Enter: + +- 0SP + 11 22 33 44 .S + ROT .S + +The stack diagram for ROT is, therefore: + +**`ROT ( a b c -- b c a , ROTate third item to top ) `** + +You have now learned the more important stack manipulation words. You +will see these in almost every Forth program. I should caution you that +if you see too many stack manipulation words being used in your code +then you may want to reexamine and perhaps reorganize your code. You +will often find that you can avoid excessive stack manipulations by +using *local or global VARIABLES* which will be discussed later. + +If you want to grab any arbitrary item on the stack, use **PICK** . Try +entering: + +- 0SP + 14 13 12 11 10 + 3 PICK . ( prints 13 ) + 0 PICK . ( prints 10 ) + 4 PICK . + +PICK makes a copy of the Nth item on the stack. The numbering starts +with zero, therefore: + +- `0 PICK is equivalent to DUP`\ + `1 PICK is equivalent to OVER` + +**`PICK ( ... v3 v2 v1 v0 N -- ... v3 v2 v1 v0 vN ) `** + +(Warning. The Forth-79 and FIG Forth standards differ from the ANS and +Forth \'83 standard in that their PICK numbering starts with one, not +zero.) + +I have included the stack diagrams for some other useful stack +manipulation words. Try experimenting with them by putting numbers on +the stack and calling them to get a feel for what they do. Again, the +text in parentheses is just a comment and need not be entered. + +**`DROP ( n -- , remove top of stack ) `** + +**`?DUP ( n -- n n | 0 , duplicate only if non-zero, '|' means OR ) `** + +**`-ROT ( a b c -- c a b , rotate top to third position ) `** + +**`2SWAP ( a b c d -- c d a b , swap pairs ) `** + +**`2OVER ( a b c d -- a b c d a b , leapfrog pair ) `** + +**`2DUP ( a b -- a b a b , duplicate pair ) `** + +**`2DROP ( a b -- , remove pair ) `** + +**`NIP ( a b -- b , remove second item from stack ) `** + +**`TUCK ( a b -- b a b , copy top item to third position ) `** + +### []{#Problems - Stack}Problems: + +Start each problem by entering: + +- 0SP 11 22 33 + +Then use the stack manipulation words you have learned to end up with +the following numbers on the stack: + +- 1) 11 33 22 22 + + 2) 22 33 + + 3) 22 33 11 11 22 + + 4) 11 33 22 33 11 + + 5) 33 11 22 11 22 + +[Answers to the problems](#Answers%20to%20Problems) can be found at the +end of this tutorial. + +## []{#Arithmetic}Arithmetic + +Great joy can be derived from simply moving numbers around on a stack. +Eventually, however, you\'ll want to do something useful with them. This +section describes how to perform arithmetic operations in Forth. + +The Forth arithmetic operators work on the numbers currently on top of +the stack. If you want to add the top two numbers together, use the +Forth word **+** , pronounced \"plus\". Enter: + +- 2 3 + . + 2 3 + 10 + . + +This style of expressing arithmetic operations is called *Reverse Polish +Notation,* or *RPN*. It will already be familiar to those of you with HP +calculators. In the following examples, I have put the algebraic +equivalent representation in a comment. + +Some other arithmetic operators are **- \* /** . Enter: + +- 30 5 - . ( 25=30-5 ) + 30 5 / . ( 6=30/5 ) + 30 5 * . ( 150=30*5 ) + 30 5 + 7 / . \ 5=(30+5)/7 + +Some combinations of operations are very common and have been coded in +assembly language for speed. For example, **2\*** is short for 2 \* . +You should use these whenever possible to increase the speed of your +program. These include: + +- 1+ 1- 2+ 2- 2* 2/ + +Try entering: + +- 10 1- . + 7 2* 1+ . ( 15=7*2+1 ) + +One thing that you should be aware of is that when you are doing +division with integers using / , the remainder is lost. Enter: + +- 15 5 / . + 17 5 / . + +This is true in all languages on all computers. Later we will examine +**/MOD** and **MOD** which do give the remainder. + +## []{#Defining a New Word}Defining a New Word + +It\'s now time to write a *small program* in Forth. You can do this by +defining a new word that is a combination of words we have already +learned. Let\'s define and test a new word that takes the average of two +numbers. + +We will make use of two new words, **:** ( \"colon\"), and **;** ( +\"semicolon\") . These words start and end a typical *Forth definition*. +Enter: + +- : AVERAGE ( a b -- avg ) + 2/ ; + +Congratulations. You have just written a Forth program. Let\'s look more +closely at what just happened. The colon told Forth to add a new word to +its list of words. This list is called the Forth dictionary. The name of +the new word will be whatever name follows the colon. Any Forth words +entered after the name will be compiled into the new word. This +continues until the semicolon is reached which finishes the definition. + +Let\'s test this word by entering: + +- 10 20 AVERAGE . ( should print 15 ) + +Once a word has been defined, it can be used to define more words. +Let\'s write a word that tests our word.. Enter: + +- : TEST ( --) 50 60 AVERAGE . ; + TEST + +Try combining some of the words you have learned into new Forth +definitions of your choice. If you promise not to be overwhelmed, you +can get a list of the words that are available for programming by +entering: + +- WORDS + +Don\'t worry, only a small fraction of these will be used directly in +your programs. + +## []{#More Arithmetic}More Arithmetic + +When you need to know the remainder of a divide operation. /MOD will +return the remainder as well as the quotient. the word MOD will only +return the remainder. Enter: + +- 0SP + 53 10 /MOD .S + 0SP + 7 5 MOD .S + +Two other handy words are **MIN** and **MAX** . They accept two numbers +and return the MINimum or MAXimum value respectively. Try entering the +following: + +- 56 34 MAX . + 56 34 MIN . + -17 0 MIN . + +Some other useful words are: + +**`ABS ( n -- abs(n) , absolute value of n ) `** + +**`NEGATE ( n -- -n , negate value, faster then -1 * ) `** + +**`LSHIFT ( n c -- n<<c , left shift of n ) `** + +**`RSHIFT ( n c -- n>>c , logical right shift of n ) `** + +**`ARSHIFT ( n c -- n>>c ) , arithmetic right shift of n ) `** + +ARSHIFT or LSHIFT can be used if you have to multiply quickly by a power +of 2 . A right shift is like doing a divide by 2. This is often faster +than doing a regular multiply or divide. Try entering: + +- : 256* 8 LSHIFT ; + 3 256* . + +### []{#Arithmetic Overflow}Arithmetic Overflow + +If you are having problems with your calculation overflowing the 32-bit +precision of the stack, then you can use **\*/** . This produces an +intermediate result that is 64 bits long. Try the following three +methods of doing the same calculation. Only the one using \*/ will yield +the correct answer, 5197799. + +- 34867312 99154 * 665134 / . + 34867312 665134 / 99154 * . + 34867312 99154 665134 */ . + +#### []{#Convert Algebraic Expressions to Forth}Convert Algebraic Expressions to Forth + +How do we express complex algebraic expressions in Forth? For example: +20 + (3 \* 4) + +To convert this to Forth you must order the operations in the order of +evaluation. In Forth, therefore, this would look like: + +- 3 4 * 20 + + +Evaluation proceeds from left to right in Forth so there is no +ambiguity. Compare the following algebraic expressions and their Forth +equivalents: (Do **not** enter these!) + +- (100+50)/2 ==> 100 50 + 2/ + ((2*7) + (13*5)) ==> 2 7 * 13 5 * + + +If any of these expressions puzzle you, try entering them one word at a +time, while viewing the stack with .S . + +### []{#Problems - Square}Problems: + +Convert the following algebraic expressions to their equivalent Forth +expressions. (Do **not** enter these because they are not Forth code!) + +- (12 * ( 20 - 17 )) + + (1 - ( 4 * (-18) / 6) ) + + ( 6 * 13 ) - ( 4 * 2 * 7 ) + +Use the words you have learned to write these new words: + +- SQUARE ( N -- N*N , calculate square ) + + DIFF.SQUARES ( A B -- A*A-B*B , difference of squares ) + + AVERAGE4 ( A B C D -- [A+B+C+D]/4 ) + + HMS>SECONDS ( HOURS MINUTES SECONDS -- TOTAL-SECONDS , convert ) + +[Answers to the problems](#Answers%20to%20Problems) can be found at the +end of this tutorial. + +## []{#Character Input and Output}Character Input and Output + +The numbers on top of the stack can represent anything. The top number +might be how many blue whales are left on Earth or your weight in +kilograms. It can also be an ASCII character. Try entering the +following: + +- 72 EMIT 105 EMIT + +You should see the word \"Hi\" appear before the OK. The 72 is an ASCII +\'H\' and 105 is an \'i\'. EMIT takes the number on the stack and +outputs it as a character. If you want to find the ASCII value for any +character, you can use the word ASCII . Enter: + +- CHAR W . + CHAR % DUP . EMIT + CHAR A DUP . + 32 + EMIT + +Here is a complete [ASCII chart](http://www.asciitable.com/). + +Notice that the word CHAR is a bit unusual because its input comes not +from the stack, but from the following text. In a stack diagram, we +represent that by putting the input in angle brackets, \<input\>. Here +is the stack diagram for CHAR. + +**`CHAR ( <char> -- char , get ASCII value of a character ) `** + +Using EMIT to output character strings would be very tedious. Luckily +there is a better way. Enter: + +- : TOFU ." Yummy bean curd!" ; + TOFU + +The word **.\"** , pronounced \"dot quote\", will take everything up to +the next quotation mark and print it to the screen. Make sure you leave +a space after the first quotation mark. When you want to have text begin +on a new line, you can issue a carriage return using the word **CR** . +Enter: + +- : SPROUTS ." Miniature vegetables." ; + : MENU + CR TOFU CR SPROUTS CR + ; + MENU + +You can emit a blank space with **SPACE** . A number of spaces can be +output with SPACES . Enter: + +- CR TOFU SPROUTS + CR TOFU SPACE SPROUTS + CR 10 SPACES TOFU CR 20 SPACES SPROUTS + +For character input, Forth uses the word **KEY** which corresponds to +the word EMIT for output. KEY waits for the user to press a key then +leaves its value on the stack. Try the following. + +- : TESTKEY ( -- ) + ." Hit a key: " KEY CR + ." That = " . CR + ; + TESTKEY + +\[Note: On some computers, the input if buffered so you will need to hit +the ENTER key after typing your character.\] + +**`EMIT ( char -- , output character ) `** + +**`KEY ( -- char , input character ) `** + +**`SPACE ( -- , output a space ) `** + +**`SPACES ( n -- , output n spaces ) `** + +**`CHAR ( <char> -- char , convert to ASCII ) `** + +**`CR ( -- , start new line , carriage return ) `** + +**`." ( -- , output " delimited text ) `** + +## []{#Compiling from Files}Compiling from Files + +PForth can read read from ordinary text files so you can use any editor +that you wish to write your programs. + +### Sample Program + +Enter into your file, the following code. + +- \ Sample Forth Code + \ Author: your name + + : SQUARE ( n -- n*n , square number ) + DUP * + ; + + : TEST.SQUARE ( -- ) + CR ." 7 squared = " + 7 SQUARE . CR + ; + +Now save the file to disk. + +The text following the **\\** character is treated as a comment. This +would be a REM statement in BASIC or a /\*\-\--\*/ in \'C\'. The text in +parentheses is also a comment. + +### Using INCLUDE + +\"INCLUDE\" in Forth means to compile from a file. + +You can compile this file using the INCLUDE command. If you saved your +file as WORK:SAMPLE, then compile it by entering: + +- INCLUDE SAMPLE.FTH + +Forth will compile your file and tell you how many bytes it has added to +the dictionary. To test your word, enter: + +- TEST.SQUARE + +Your two words, SQUARE and TEST.SQUARE are now in the Forth dictionary. +We can now do something that is very unusual in a programming language. +We can \"uncompile\" the code by telling Forth to **FORGET** it. Enter: + +- FORGET SQUARE + +This removes SQUARE and everything that follows it, ie. TEST.SQUARE, +from the dictionary. If you now try to execute TEST.SQUARE it won\'t be +found. + +Now let\'s make some changes to our file and reload it. Go back into the +editor and make the following changes: (1) Change TEST.SQUARE to use 15 +instead of 7 then (2) Add this line right before the definition of +SQUARE: + +- ANEW TASK-SAMPLE.FTH + +Now Save your changes and go back to the Forth window. + +You\'re probably wondering what the line starting with **ANEW** was for. +ANEW is always used at the beginning of a file. It defines a special +marker word in the dictionary before the code. The word typically has +\"TASK-\" as a prefix followed by the name of the file. When you +ReInclude a file, ANEW will automatically FORGET the old code starting +after the ANEW statement. This allows you to Include a file over and +over again without having to manually FORGET the first word. If the code +was not forgotten, the dictionary would eventually fill up. + +If you have a big project that needs lots of files, you can have a file +that will load all the files you need. Sometimes you need some code to +be loaded that may already be loaded. The word **INCLUDE?** will only +load code if it isn\'t already in the dictionary. In this next example, +I assume the file is on the volume WORK: and called SAMPLE. If not, +please substitute the actual name. Enter: + +- FORGET TASK-SAMPLE.FTH + INCLUDE? SQUARE WORK:SAMPLE + INCLUDE? SQUARE WORK:SAMPLE + +Only the first INCLUDE? will result in the file being loaded. + +## []{#Variables}Variables + +Forth does not rely as heavily on the use of variables as other compiled +languages. This is because values normally reside on the stack. There +are situations, of course, where variables are required. To create a +variable, use the word **VARIABLE** as follows: + +- VARIABLE MY-VAR + +This created a variable named MY-VAR . A space in memory is now reserved +to hold its 32-bit value. The word VARIABLE is what\'s known as a +\"defining word\" since it creates new words in the dictionary. Now +enter: + +- MY-VAR . + +The number you see is the address, or location, of the memory that was +reserved for MY-VAR. To store data into memory you use the word **!** , +pronounced \"store\". It looks like an exclamation point, but to a Forth +programmer it is the way to write 32-bit data to memory. To read the +value contained in memory at a given address, use the Forth word **@** , +pronounced \"fetch\". Try entering the following: + +- 513 MY-VAR ! + MY-VAR @ . + +This sets the variable MY-VAR to 513 , then reads the value back and +prints it. The stack diagrams for these words follows: + +**`@ ( address -- value , FETCH value FROM address in memory ) `** + +**`! ( value address -- , STORE value TO address in memory )`** + +**`VARIABLE ( <name> -- , define a 4 byte memory storage location)`** + +A handy word for checking the value of a variable is **?** , pronounced +\"question\". Try entering: + +- MY-VAR ? + +If ? wasn\'t defined, we could define it as: + +- : ? ( address -- , look at variable ) + @ . + ; + +Imagine you are writing a game and you want to keep track of the highest +score. You could keep the highest score in a variable. When you reported +a new score, you could check it aginst the highest score. Try entering +this code in a file as described in the previous section: + +- VARIABLE HIGH-SCORE + + : REPORT.SCORE ( score -- , print out score ) + DUP CR ." Your Score = " . CR + HIGH-SCORE @ MAX ( calculate new high ) + DUP ." Highest Score = " . CR + HIGH-SCORE ! ( update variable ) + ; + +Save the file to disk, then compile this code using the INCLUDE word. +Test your word as follows: + +- 123 REPORT.SCORE + 9845 REPORT.SCORE + 534 REPORT.SCORE + +The Forth words @ and ! work on 32-bit quantities. Some Forths are +\"16-bit\" Forths. They fetch and store 16-bit quantities. Forth has +some words that will work on 8 and 16-bit values. C@ and C! work +characters which are usually for 8-bit bytes. The \'C\' stands for +\"Character\" since ASCII characters are 8-bit numbers. Use W@ and W! +for 16-bit \"Words.\" + +Another useful word is **+!** , pronounced \"plus store.\" It adds a +value to a 32-bit value in memory. Try: + +- 20 MY-VAR ! + 5 MY-VAR +! + MY-VAR @ . + +Forth also provides some other words that are similar to VARIABLE. Look +in the glossary for VALUE and ARRAY. Also look at the section on +\"[local variables](pf_ref.php#Local%20Variables%20%7B%20foo%20--%7D?)\" +which are variables which only exist on the stack while a Forth word is +executing. + +*A word of warning about fetching and storing to memory*: You have now +learned enough about Forth to be dangerous. The operation of a computer +is based on having the right numbers in the right place in memory. You +now know how to write new numbers to any place in memory. Since an +address is just a number, you could, but shouldn\'t, enter: + +- 73 253000 ! ( Do NOT do this. ) + +The 253000 would be treated as an address and you would set that memory +location to 73. I have no idea what will happen after that, maybe +nothing. This would be like firing a rifle through the walls of your +apartment building. You don\'t know who or what you are going to hit. +Since you share memory with other programs including the operating +system, you could easily cause the computer to behave strangely, even +crash. Don\'t let this bother you too much, however. Crashing a +computer, unlike crashing a car, does not hurt the computer. You just +have to reboot. The worst that could happen is that if you crash while +the computer is writing to a disk, you could lose a file. That\'s why we +make backups. This same potential problem exists in any powerful +language, not just Forth. This might be less likely in BASIC, however, +because BASIC protects you from a lot of things, including the danger of +writing powerful programs. + +Another way to get into trouble is to do what\'s called an \"odd address +memory access.\" The 68000 processor arranges words and longwords, 16 +and 32 bit numbers, on even addresses. If you do a **@** or **!** , or +**W@** or **W!** , to an odd address, the 68000 processor will take +exception to this and try to abort. + +Forth gives you some protection from this by trapping this exception and +returning you to the OK prompt. If you really need to access data on an +odd address, check out the words **ODD@** and **ODD!** in the glossary. +**C@** and **C!** work fine on both odd and even addresses. + +## []{#Constants}Constants + +If you have a number that is appearing often in your program, we +recommend that you define it as a \"constant.\" Enter: + +- 128 CONSTANT MAX_CHARS + MAX_CHARS . + +We just defined a word called MAX_CHARS that returns the value on the +stack when it was defined. It cannot be changed unless you edit the +program and recompile. Using **CONSTANT** can improve the readability of +your programs and reduce some bugs. Imagine if you refer to the number +128 very often in your program, say 8 times. Then you decide to change +this number to 256. If you globally change 128 to 256 you might change +something you didn\'t intend to. If you change it by hand you might miss +one, especially if your program occupies more than one file. Using +CONSTANT will make it easy to change. The code that results is equally +as fast and small as putting the numbers in directly. I recommend +defining a constant for almost any number. + +## []{#Logical Operators}Logical Operators + +These next two sections are concerned with decision making. This first +section deals with answering questions like \"Is this value too large?\" +or \"Does the guess match the answer?\". The answers to questions like +these are either TRUE or FALSE. Forth uses a 0 to represent **FALSE** +and a -1 to represent **TRUE**. TRUE and FALSE have been capitalized +because they have been defined as Forth constants. Try entering: + +- 23 71 = . + 18 18 = . + +You will notice that the first line printed a 0, or FALSE, and the +second line a -1, or TRUE. The equal sign in Forth is used as a +question, not a statement. It asks whether the top two items on the +stack are equal. It does not set them equal. There are other questions +that you can ask. Enter: + +- 23 198 < . + 23 198 > . + 254 15 > . + +In California, the drinking age for alcohol is 21. You could write a +simple word now to help bartenders. Enter: + +- : DRINK? ( age -- flag , can this person drink? ) + 20 > + ; + + 20 DRINK? . + 21 DRINK? . + 43 DRINK? . + +The word FLAG in the stack diagram above refers to a logical value. + +Forth provides special words for comparing a number to 0. They are +**0=** **0\>** and **0\<** . Using 0\> is faster than calling 0 and \> +separately. Enter: + +- `23 0> . ( print -1 )`\ + `-23 0> . ( print 0 )`\ + `23 0= . ( print 0 )` + +For more complex decisions, you can use the *Boolean* operators **OR** , +**AND** , and **NOT** . OR returns a TRUE if either one or both of the +top two stack items are true. + +- TRUE TRUE OR . + TRUE FALSE OR . + FALSE FALSE OR . + +AND only returns a TRUE if both of them are true. + +- TRUE TRUE AND . + TRUE FALSE AND . + +NOT reverses the value of the flag on the stack. Enter: + +- TRUE . + TRUE NOT . + +Logical operators can be combined. + +- 56 3 > 56 123 < AND . + 23 45 = 23 23 = OR . + +Here are stack diagrams for some of these words. See the glossary for a +more complete list. + +**`< ( a b -- flag , flag is true if A is less than B )`** + +**`> ( a b -- flag , flag is true if A is greater than B )`** + +**`= ( a b -- flag , flag is true if A is equal to B )`** + +**`0= ( a -- flag , true if a equals zero )`** + +**`OR ( a b -- a||b , perform logical OR of bits in A and B )`** + +**`AND ( a b -- a&b , perform logical AND of bits in A and B )`** + +**`NOT ( flag -- opposite-flag , true if false, false if true )`** + +### []{#Problems - Logical}Problems: + +1\) Write a word called LOWERCASE? that returns TRUE if the number on +top of the stack is an ASCII lowercase character. An ASCII \'a\' is 97 . +An ASCII \'z\' is 122 . Test using the characters \" A \` a q z { \". + +- CHAR A LOWERCASE? . ( should print 0 ) + CHAR a LOWERCASE? . ( should print -1 ) + +[Answers to the problems](#Answers%20to%20Problems) can be found at the +end of this tutorial. + +## []{#Conditionals - IF ELSE THEN CASE}Conditionals - IF ELSE THEN CASE + +You will now use the TRUE and FALSE flags you learned to generate in the +last section. The \"flow of control\" words accept flags from the stack, +and then possibly \"branch\" depending on the value. Enter the following +code. + +- : .L ( flag -- , print logical value ) + IF ." True value on stack!" + ELSE ." False value on stack!" + THEN + ; + + 0 .L + FALSE .L + TRUE .L + 23 7 < .L + +You can see that when a TRUE was on the stack, the first part got +executed. If a FALSE was on the stack, then the first part was skipped, +and the second part was executed. One thing you will find interesting is +that if you enter: + +- 23 .L + +the value on the stack will be treated as true. The flow of control +words consider any value that does not equal zero to be TRUE. + +The **ELSE** word is optional in the **IF\...THEN** construct. Try the +following: + +- : BIGBUCKS? ( amount -- ) + 1000 > + IF ." That's TOO expensive!" + THEN + ; + + 531 BIGBUCKS? + 1021 BIGBUCKS? + +Many Forths also support a **CASE** statement similar to switch() in +\'C\'. Enter: + +- : TESTCASE ( N -- , respond appropriately ) + CASE + 0 OF ." Just a zero!" ENDOF + 1 OF ." All is ONE!" ENDOF + 2 OF WORDS ENDOF + DUP . ." Invalid Input!" + ENDCASE CR + ; + + 0 TESTCASE + 1 TESTCASE + 5 TESTCASE + +See CASE in the glossary for more information. + +### []{#Problems - Conditionals}Problems: + +1\) Write a word called DEDUCT that subtracts a value from a variable +containing your checking account balance. Assume the balance is in +dollars. Print the balance. Print a warning if the balance is negative. + +- VARIABLE ACCOUNT + + : DEDUCT ( n -- , subtract N from balance ) + ????????????????????????????????? ( you fill this in ) + ; + + 300 ACCOUNT ! ( initial funds ) + 40 DEDUCT ( prints 260 ) + 200 DEDUCT ( print 60 ) + 100 DEDUCT ( print -40 and give warning! ) + +[Answers to the problems](#Answers%20to%20Problems) can be found at the +end of this tutorial. + +## []{#Loops}Loops + +Another useful pair of words is **BEGIN\...UNTIL** . These are used to +loop until a given condition is true. Try this: + +- : COUNTDOWN ( N -- ) + BEGIN + DUP . CR ( print number on top of stack ) + 1- DUP 0< ( loop until we go negative ) + UNTIL + ; + + 16 COUNTDOWN + +This word will count down from N to zero. + +If you know how many times you want a loop to execute, you can use the +**DO\...LOOP** construct. Enter: + +- : SPELL + ." ba" + 4 0 DO + ." na" + LOOP + ; + +This will print \"ba\" followed by four occurrences of \"na\". The +ending value is placed on the stack before the beginning value. Be +careful that you don\'t pass the values in reverse. Forth will go \"the +long way around\" which could take awhile. The reason for this order is +to make it easier to pass the loop count into a word on the stack. +Consider the following word for doing character graphics. Enter: + +- : PLOT# ( n -- ) + 0 DO + [CHAR] - EMIT + LOOP CR + ; + + CR 9 PLOT# 37 PLOT# + +If you want to access the loop counter you can use the word I . Here is +a simple word that dumps numbers and their associated ASCII characters. + +- : .ASCII ( end start -- , dump characters ) + DO + CR I . I EMIT + LOOP CR + ; + + 80 64 .ASCII + +If you want to leave a DO LOOP before it finishes, you can use the word +**LEAVE**. Enter: + +- : TEST.LEAVE ( -- , show use of leave ) + 100 0 + DO + I . CR \ print loop index + I 20 > \ is I over 20 + IF + LEAVE + THEN + LOOP + ; + TEST.LEAVE \ will print 0 to 20 + +Please consult the manual to learn about the following words **+LOOP** +and **RETURN** . FIXME + +Another useful looping construct is the **BEGIN WHILE REPEAT** loop. +This allows you to make a test each time through the loop before you +actually do something. The word WHILE will continue looping if the flag +on the stack is True. Enter: + +- : SUM.OF.N ( N -- SUM[N] , calculate sum of N integers ) + 0 \ starting value of SUM + BEGIN + OVER 0> \ Is N greater than zero? + WHILE + OVER + \ add N to sum + SWAP 1- SWAP \ decrement N + REPEAT + SWAP DROP \ get rid on N + ; + + 4 SUM.OF.N \ prints 10 ( 1+2+3+4 ) + +### []{#Problems - Loops}Problems: + +1\) Rewrite SUM.OF.N using a DO LOOP. + +2\) Rewrite SUM.OF.N using BEGIN UNTIL. + +3\) For bonus points, write SUM.OF.N without using any looping or +conditional construct! + +[Answers to the problems](#Answers%20to%20Problems) can be found at the +end of this tutorial. + +## []{#Text Input and Output}Text Input and Output + +You learned earlier how to do single character I/O. This section +concentrates on using strings of characters. You can embed a text string +in your program using S\". Note that you must follow the S\" by one +space. The text string is terminated by an ending \" .Enter: + +- : TEST S" Hello world!" ; + TEST .S + +Note that TEST leaves two numbers on the stack. The first number is the +address of the first character. The second number is the number of +characters in the string. You can print the characters of the string as +follows. + +- TEST DROP \ get rid of number of characters + DUP C@ EMIT \ prints first character, 'H' + CHAR+ DUP C@ EMIT \ prints second character, 'e' + \ and so on + +CHAR+ advances the address to the next character. You can print the +entire string using TYPE. + +- TEST TYPE + TEST 2/ TYPE \ print half of string + +It would be nice if we could simply use a single address to describe a +string and not have to pass the number of characters around. \'C\' does +this by putting a zero at the end of the string to show when it ends. +Forth has a different solution. A text string in Forth consists of a +character count in the first byte, followed immediately by the +characters themselves. This type of character string can be created +using the Forth word C\" , pronounced \'c quote\'. Enter: + +- : T2 C" Greetings Fred" ; + T2 . + +The number that was printed was the address of the start of the string. +It should be a byte that contains the number of characters. Now enter: + +- T2 C@ . + +You should see a 14 printed. Remember that C@ fetches one character/byte +at the address on the stack. You can convert a counted Forth string to +an address and count using COUNT. + +- T2 COUNT .S + TYPE + +The word **COUNT** extracts the number of characters and their starting +address. COUNT will only work with strings of less than 256 characters, +since 255 is the largest number that can be stored in the count byte. +TYPE will, however, work with longer strings since the length is on the +stack. Their stack diagrams follow: + +**`CHAR+ ( address -- address' , add the size of one character )`** + +**`COUNT ( $addr -- addr #bytes , extract string information ) `** + +**`TYPE ( addr #bytes -- , output characters at addr )`** + +The \$addr is the address of a count byte. The dollar sign is often used +to mark words that relate to strings. + +You can easily input a string using the word **ACCEPT**. (You may want +to put these upcoming examples in a file since they are very handy.) The +word **ACCEPT** receives characters from the keyboard and places them at +any specified address. **ACCEPT** takes input characters until a maximum +is reached or an end of line character is entered. **ACCEPT** returns +the number of characters entered. You can write a word for entering +text. Enter: + +- : INPUT$ ( -- $addr ) + PAD 1+ ( leave room for byte count ) + 127 ACCEPT ( recieve a maximum of 127 chars ) + PAD C! ( set byte count ) + PAD ( return address of string ) + ; + + INPUT$ COUNT TYPE + +Enter a string which should then be echoed. You could use this in a +program that writes form letters. + +- : FORM.LETTER ( -- ) + ." Enter customer's name." CR + INPUT$ + CR ." Dear " DUP COUNT TYPE CR + ." Your cup that says " COUNT TYPE + ." is in the mail!" CR + ; + +**`ACCEPT ( addr maxbytes -- numbytes , input text, save at address ) `** + +You can use your word INPUT\$ to write a word that will read a number +from the keyboard. Enter: + +- : INPUT# ( -- N true | false ) + INPUT$ ( get string ) + NUMBER? ( convert to a string if valid ) + IF DROP TRUE ( get rid of high cell ) + ELSE FALSE + THEN + ; + +This word will return a single-precision number and a TRUE, or it will +just return FALSE. The word **NUMBER?** returns a double precision +number if the input string contains a valid number. Double precision +numbers are 64-bit so we DROP the top 32 bits to get a single-precision +32 bit number. + +## []{#Changing Numeric Base}Changing Numeric Base + +For day-to-day life, the numbering system we use is decimal, or \"base +10.\" That means each digit get multiplied by a power of 10. Thus a +number like 527 is equal to (5\*100 + 2\*10 + 7\*1). The use of 10 for +the numeric base is a completely arbitrary decision. It no doubt has +something to do with the fact that most people have 10 fingers +(including thumbs). The Babylonians used base 60, which is where we got +saddled with the concept of 60 minutes in an hour. Computer hardware +uses base 2, or \"binary\". The computer number \"1101\" is equal to +(1\*8 + 1\*4 + 0\*2 + 1\*1). If you add these up, you get 8+4+1=13 . The +binary number \"10\" is (1\*2 + 0\*1), or 2. Likewise the numeric string +\"10\" in any base N is N. + +Forth makes it very easy to explore different numeric bases because it +can work in any base. Try entering the following: + +- DECIMAL 6 BINARY . + 1 1 + . + 1101 DECIMAL . + +Another useful numeric base is *hexadecimal*. which is base 16. One +problem with bases over 10 is that our normal numbering system only has +digits 0 to 9. For hex numbers we use the letters A to F for the digits +10 to 15. Thus the hex number \"3E7\" is equal to (3\*256 + 14\*16 + +7\*1). Try entering: + +- DECIMAL 12 HEX . \ print C + DECIMAL 12 256 * 7 16 * + 10 + .S + DUP BINARY . + HEX . + +A variable called **BASE** is used to keep track of the current numeric +base. The words **HEX** , **DECIMAL** , and **BINARY** work by changing +this variable. You can change the base to anything you want. Try: + +- 7 BASE ! + 6 1 + . + BASE @ . \ surprise! + +You are now in base 7 . When you fetched and printed the value of BASE, +it said \"10\" because 7, in base 7, is \"10\". + +PForth defines a word called .HEX that prints a number as hexadecimal +regardless of the current base. + +- DECIMAL 14 .HEX + +You could define a word like .HEX for any base. What is needed is a way +to temporarily set the base while a number is printed, then restore it +when we are through. Try the following word: + +- : .BIN ( N -- , print N in Binary ) + BASE @ ( save current base ) + 2 BASE ! ( set to binary ) + SWAP . ( print number ) + BASE ! ( restore base ) + ; + + DECIMAL + 13 .BIN + 13 . + +## []{#Answers to Problems}Answers to Problems + +If your answer doesn\'t exactly match these but it works, don\'t fret. +In Forth, there are usually many ways to the same thing. + +### [Stack Manipulations](#Problems%20-%20Stack) + +- 1) SWAP DUP + 2) ROT DROP + 3) ROT DUP 3 PICK + 4) SWAP OVER 3 PICK + 5) -ROT 2DUP + +### [Arithmetic](#Problems%20-%20Square) + +- (12 * (20 - 17)) ==> 20 17 - 12 * + (1 - (4 * (-18) / 6)) ==> 1 4 -18 * 6 / - + (6 * 13) - (4 * 2 * 7) ==> 6 13 * 4 2 * 7 * - + + : SQUARE ( N -- N*N ) + DUP * + ; + + : DIFF.SQUARES ( A B -- A*A-B*B ) + SWAP SQUARE + SWAP SQUARE - + ; + + : AVERAGE4 ( A B C D -- [A+B+C+D]/4 ) + + + + ( add'em up ) + 4 / + ; + + : HMS>SECONDS ( HOURS MINUTES SECONDS -- TOTAL-SECONDS ) + -ROT SWAP ( -- seconds minutes hours ) + 60 * + ( -- seconds total-minutes ) + 60 * + ( -- seconds ) + ; + +### [Logical Operators](#Problems%20-%20Logical) + +- : LOWERCASE? ( CHAR -- FLAG , true if lowercase ) + DUP 123 < + SWAP 96 > AND + ; + +### [Conditionals](#Problems%20-%20Conditionals) + +- : DEDUCT ( n -- , subtract from account ) + ACCOUNT @ ( -- n acc ) + SWAP - DUP ACCOUNT ! ( -- acc' , update variable ) + ." Balance = $" DUP . CR ( -- acc' ) + 0< ( are we broke? ) + IF ." Warning!! Your account is overdrawn!" CR + THEN + ; + +### [`Loops`](#Problems%20-%20Loops) + +- : SUM.OF.N.1 ( N -- SUM[N] ) + 0 SWAP \ starting value of SUM + 1+ 0 \ set indices for DO LOOP + ?DO \ safer than DO if N=0 + I + + LOOP + ; + + : SUM.OF.N.2 ( N -- SUM[N] ) + 0 \ starting value of SUM + BEGIN ( -- N' SUM ) + OVER + + SWAP 1- SWAP + OVER 0< + UNTIL + SWAP DROP + ; + + : SUM.OF.N.3 ( NUM -- SUM[N] , Gauss' method ) + DUP 1+ \ SUM(N) = N*(N+1)/2 + * 2/ + ; + +Back to [pForth Home Page](../pforth)\ +::: + +::: {#footer} +\(C\) 1997-2015 Mobileer Inc - All Rights Reserved - [Contact +Us](/contacts.php) +::: +::::::::::::: |