FORTH GUIDE
CHAPTER VIII
DEFINING WORDS REVISITED
Earlier we discussed the three basic defining words: 'colon', CONSTANT, and VARIABLE. MVP-FORTH includes two others: user variables and code definitions. The latter will be discussed with the assembler. The space for user-variables was fixed in the source for the system and is not easily changed.
User variables, return the address containing the named variable. To this extent it appears exactly like any other variable. However, the location is defined in the MVP-FORTH dictionary as an offset from the base address of all of the user variables. This base address is set up in high memory when FORTH is loaded. Initial values are moved from low memory to high memory on start up.
The more interesting capability of MVP-FORTH is the ability to add your own defining words. In essence, this gives you the power of interactively adding to the FORTH compiler. There are few other languages with this power. It means that you can define your own data types and structures as needed for your application. You are not forced into using only those data types and structures provided by the native language. It also means that you do not have to include any more data types and structure than those required for your application.
There are three steps to the process of adding and using new defining words. You must write the new defining word, you must use the new defining word to define new words and finally you can execute the new words.
In each step, FORTH functions are executed. Therefore, each step has a run-time function. The first two steps each compiles the definition for the next step.
Step one is usually a colon definition whose function is to compile the new defining word. This is equivalent to adding to the FORTH compiler interactively. It defines the structure of the header and the contents of the data fields, and in a second part defines the ultimate function to be assigned by step two to the run time in step three.
Step two uses the defining word compiled by step one to compile a new word in the FORTH dictionary and assigns the new function to be executed by that word. This step is on a par with other defining words as CONSTANT and VARIABLE.
Step three runs the function of the newly defined word.
Step one usually uses two primitives in the colon definition of the new defining word.
They are CREATE and DOES>. Though they are usually used in colon definitions, CREATE may be used interactively.
CREATE creates a header structure for a new entry into the MVP-FORTH dictionary from the word which follows it. When the new word defined with CREATE is executed, the address of the beginning of the data field is left on the stack.
The DOES> part begins the definition of a function which will be executed each time a member of the family of new words is executed. The code field of the newly defined word as set by CREATE is modified to point to a subroutine which will execute the sequence of high level FORTH words which follow DOES> in step one.
The combination of these two words produces one of the most powerful functions available in MVP-FORTH. You should take the time to study the use of these words.
First examine CREATE alone.
CREATE XXXWhen CREATE is executed interactively, it compiles a header for a new word,
XXX, in the FORTH dictionary. No space is allotted to the data field. Examine the memory image of XXX with DUMP.
' XXX HEX 10 - 20 DUMPFirst find the data field address even though no data has been given. In this case that should be the same address as returned by HERE. Then go to hexadecimal. This is desirable because the DUMP is in hexadecimal with 10 hex bytes on each line. By going back 10 hex bytes from the data field address, you can establish a fixed location for the header structure.
The header structure will always appear right justified on the first line of the dump. When this is the case, the last two bytes will be combined as the code field. The code field contains an address which points to machine code to be executed. In this example that machine code will return the address of the data field. You will eventually learn to recognize the pointer in the code field for the 'colon', CONSTANT and VARIABLE definitions. FORTH words defined with the assembler will have the contents of the code field pointing directly to the data field which in fact contains the machine code to be run.
The two bytes preceding the code field contain a pointer to the name field of the word defined immediately before the new one. These two bytes are called the link field. They provide the link from the present word to the rest of the words in the dictionary.
Since you have defined a word with three characters, the three bytes preceding the link field will contain the character code for the name. The last byte will not look the same. It has the highest order bit set too. The fourth byte before the link field in this case will have the value 84. The first, or lowest order five bits of this byte contain the number of characters used in the name. The highest bit is set to flag the beginning of the name field. The other two bits are used for special purposes. They will be discussed later. This fourth byte before the link field is the beginning of the name field.
Each of the fields begins at an address. The code field address points to two bytes which contain the address of the machine code. The link field address contains two bytes which point to the name field. The name field address contains the beginning count byte followed by the characters in the name. It is a variable length field.
It will probably help for you to explore the rest of the FORTH dictionary in a similar manner. This is important enough to the understanding of all of FORTH, to take a short detour at this time. Try the following:
' ?CONFIGURE HEX 10 - 100 DUMPThis is a long definition. You will see that the name field begins near the beginning of the line. On the right hand side of the dump you will see ?CONFIGURE. The final E of the name ?CONFIGURE will have the 8th bit set and will not print.
You can see how all of the prompts for CONFIGURE are embedded in the data field of this word. You could use this DUMP to find the prompts which you might wish to change. Determine the code for the desired characters and stick them in with C!. This is the way the various implementations on the disk have beeen modified.
To return to your new word, XXX, you have not used any bytes in the data field. Suppose you wished to make the new word behave as a variable. You could compile a value in the dictionary in the location of the data field. The definition would then be:
CREATE XXX 0 , VARIABLE YYY 0 YYY !Now examine each of these new words. You will see that the pointer in the code field of both is the same and the data is the same. You have found two ways of defining the same function.
The contents of the code field points to a piece of assembly code which can be found in the MVP-FORTH assembly source listing as a part of the definition of CREATE. There it has been given the assembler label DOVAR. The name is not in the FORTH dictionary but you might want to add it as a constant:
' XXX CFA @ CONSTANT DOVARNow you can examine the contents of the code field address of a constant. In the MVP-FORTH assembly source listing this has been given the label DOCON. You can add to your FORTH dictionary a constant with this name.
' DOVAR CFA @ CONSTANT DOCONWhile you are examining the contents of code fields, you might take a look at that for a colon definition. In the MVP-FORTH assembly source listing the machine code segment has been given the label DOCOL. You can also add this address to your FORTH dictionary as a constant.
: NOOP ; ' NOOP CFA @ CONSTANT DOCOLDefining the word NOOP was the quickest way to have a word which you are sure is a colon definition. If you remembered one, you could have used it as well. You might find it interesting to see what these code sequences look like in the assembly source code also on this disk. They are discussed in ALL ABOUT FORTH.
The MVP-FORTH defining word USER is used to define user variables. These are located in space allocated in high memory when the program is loaded. No extra space is available in this table. Therefore, you will have no occasion to use this word to add more user variables to your dictionary. As a matter of information, you will discover in the MVP- FORTH assembly source listing that the assembly code segment for this word has the label DOUSE. The defining word creates a constant which is then interpreted with this code segment.
Now return to step one in which a defining word is defined.
The other half of the definition of a defining word, determines what it is you want each of the new family of words to do. Perhaps this statement is a little confusing with defining of defining. Try to keep in mind the steps. In time the process will become clearer. This is one of the more difficult concepts in FORTH.
The second part of step one is compiled by the function of DOES>. The high level FORTH words entered after DOES> will be compiled like any other colon definition. They must terminate with a semicolon.
Try another approach to understanding the CREATE ... DOES> structure of defining words. Examine it in connection with a word you already know - CONSTANT. When defining a defining word in stage one such as CONSTANT, the run time routine defines CONSTANT.
: CONSTANT CREATE ( n --- ) , DOES> ( --- n ) @ ;The above is the definition of a defining word which will perform exactly as the already defined CONSTANT. The run time routine makes a colon definition which has two parts: 1/ The creation of a new header in the FORTH dictionary and 2/ The storage of the function which any new word subsequently defined with this word will execute, which in this case is simply to fetch the value there.
The redefinition of CONSTANT is an example of stage one. When you use CONSTANT to define another word you are at stage two of the process. When you use the defining word CONSTANT, you are executing the run time function of the defining word.
10 CONSTANT TENIn stage two the word TEN is compiled into the FORTH dictionary. In the code field of TEN is a pointer to machine code which will start execution of the FORTH words compiled after DOES> in stage one. In this example the function is 'fetch'.
Finally, in step three the word TEN is executed. The execution starts with the address of the data field of TEN and executes the words compiled after DOES> in stage one. In the example, the single function 'fetch' is executed and the value of ten is placed on the data stack.
TEN .SA number of other permutations are possible. In the create portion you can poke into the data field with comma and C-comma, any values you wish. You can also execute any other FORTH functions you may wish. Suppose you want to keep track of the number of new words you are adding with your new defining word. You could define a COUNTER and initialize that to 0. Then, every time you use the defining word, increment the counter:
CREATE COUNTER 0 , : NEW-CONSTANT ( n --- ) CREATE 1 COUNTER +! , DOES> @ ;Another FORTH function would be to create a table containing the execution addresses of all new word definitions. You could then execute any new word by fetching the contents of the item number in the table and executing it. This technique has been used in developing language vocabulary drills.
Consider another application. Set up your FORTH dictionary as a foreign language dictionary. This requires a careful understanding of the structure and function of MVP-FORTH.
What you desire is a defining word which will allow you to add two words to the FORTH dictionary. The function of each word will be to display the other word. Stage one would begin as follows:
: ADD1 CREATE DOES> ID. 10 SPACES ;The first part of our definition defines a word which will always display the next word in the FORTH dictionary. This is the function of ID. which is already defined in MVP-FORTH. The 10 SPACES is to keep the OK from being appended directly to the displayed name.
: ADD2 CREATE DOES> LFA @ ID. 10 SPACES ;The second part of our definition defines a word which will always display the previous word defined in the FORTH dictionary. Note that in both cases nothing is placed in the data field of either word by the process of adding them to the dictionary.
: ADD ADD1 ADD2 ;When the words are combined, the data field of the first word becomes the name field of the second word. At run time, entering the first of a pair of words will take the data field address as returned from the DOES> and display the name field at that location. At run time, entering the second of a pair of words will take the pointer to the data field returned by the DOES> and move to the link field and thence to the name field of the preceding word and display that.
The new defining word, ADD, is used in stage two as follows:
ADD ONE EINYou can use ADD to add as many pairs of words to your FORTH dictionary as you wish. You will later learn how to use vocabularies in FORTH. You could then set up separate vocabularies for French, German, Spanish, etc. You might want to try that as an exercise.
Then in stage three you can execute any of your newly defined words.
EIN ONEThe amazing feature of the foreign language feature, is the minimal amount of space required to add these new data structures to your FORTH system. You also have the power to modify your compiler to accomplish the desired result. Given the same problem, it would be interesting to see how you might use any other computer programming language to accomplish the same result.
Use DUMP to examine the FORTH dictionary around ONE and EIN. Add more word pairs to your dictionary and examine them.
These are just a few illustrations of the power of defining words in MVP-FORTH. You are limited only by your ability to formulate the solution to your problem. Often that means that you are limited only by your ability to understand your problem.