FORTH GUIDE
CHAPTER VII
STACKS
You have perhaps wondered about the parameters which have been used in some of the MVP-FORTH functions. To LIST a screen you must first tell the system the number of the screen to be LISTed. Sometimes you have seen that a function both needs a value and leaves a value.
Many functions in most languages need parameters and leave parameters. MVP-FORTH uses a very simple scheme for handling such parameters. It is a little like using a pronoun in the English language. The pronoun refers to its antecedent noun. You always need to be careful to keep the references straight. Should you confuse the antecedent reference you will confuse your English statement.
One way to think about values given to your computer system is similar to providing a list of references. You need to keep the references straight. If you slip you may put garbage into your program.
The order of inputting the variables is important. The order of the parameters for a FORTH function is also important. They must match. If they do not, there is little checking done in FORTH and the system may get lost. The order of values input is the reverse of the order of use by the system. This is the scheme of last in first out - LIFO.
So long as you, the programmer, remember that the order required for specific functions and make sure that they are available in that order, there is no need to burden the system with specific names for each value. As you learn to work with such a system you might find that many applications are easily solved without naming each value given the system. The process of naming is time consuming and is really an extra step for you.
The system stores the values given it in the form of a stack. The data stack of MVP-FORTH, also referred to as the parameter stack, is often the stack used by the system processor. Most computer processors use a built in stack for keeping track of the return address on subroutine jumps and calls. Interrupt routines also make use of the system stack. The design of an efficient stack for a processor is most important. MVP-FORTH usually makes use of the underlying system stack for its data stack.
In this manner data values can be stored in the system. The system stack pointer is placed within the area of memory used by MVP-FORTH. The location is setup in the high memory of FORTH when the program is loaded. The directives are stored in low memory.
Which way the stack moves depends upon where you stand. If you see the low memory as the bottom of FORTH and high memory at the top of FORTH then you will find that most processors build the stack as stalactites rather than stalagmites. That is the stack actually grows down.
On the other hand the analogy of cafeteria trays as a stack with the stack growing up has often been used. Which ever way you conceive of the stack you will refer to the top of the stack. Actually, the stack does not really move. All that moves is the value in a pointer to the 'top of the stack'. When a value is given the system, it is stored in the location pointed to and the location is incremented. All elements in the usual stack are 16-bit and thus take two address locations. The pointer is incremented or decremented by 2.
The stack then is an array of bytes. The beginning of the stack is the reference, and the pointer moves to successively lower addresses as values are added to the stack and increased as values are removed from the stack. The program automatically handles the pointer. But in MVP-FORTH, you have access to the origin of the stack. The initial value of the stack pointer is stored in the user variable ( a special type of variable ) SP0. The function, S0, fetches the value of SP0 and leaves it available for use.
SP0 is often referred to as the 'bottom of the stack'. Be careful with the image you have of the function of the stack. The bottom of the stack is the highest memory address possible in the stack.
In MVP-FORTH, the data stack moves down from high memory toward the top of the dictionary. The size of the dictionary is limited by the number of values to be used on the stack. Likewise the size of the stack is limited by the size of the dictionary.
You can inspect the stack any time you wish. To prevent changing the stack, your definition cannot add to the stack or subtract from it. You need to select a value far enough below the initial stack pointer to include all possible values on the stack.
: DUMP-STACK S0 128 - 128 DUMP ;This function will dump a large segment of memory below the bottom of the stack and display it for you. Enter a sequence of digits which you can recognize and then DUMP-STACK and find them. Now try printing some of the definitions with a series of 'dot' commands and then try DUMP-STACK again.
You now see what is actually happening with the stack when you enter things. You will notice that the DUMP function makes extensive use of the stack. To make efficient use of the stack as a temporary store of values without specific names and to pass them to FORTH functions, it is well to visualize this structure. But if all of this is too much for you, it is not really necessary.
There are a few more FORTH functions which help in understanding the stack. You can always find the number of items on the stack with the function DEPTH. The value returned is the number of items on the stack before the number is returned. You will see a problem DUMPing the stack while working on the stack. You can also find the value currently being pointed to by the stack pointer, ( SP@ ). This is defined as a code routine because the stack pointer of the system is used. It is a system register which must be moved to the stack.
The .S function uses some of these more primitive functions in a non-destructive display of the contents of the stack. It is much easier than using DUMP-STACK which you defined above.
There will be times when the order on the stack is not the order you want for a particular function. Or perhaps you will want to preserve a value on the stack which the function will consume. Or for any one of a number of other reasons, you will want to manipulate the stack.
You have already used a few of these functions. DUP is perhaps one of the most common stack function. You will sometimes see the functions which operate on the stack called stack operators. This does just what it says: it duplicates the value on the top of the stack. You can satisfy yourself of the functions by executing them one at a time and using the .S function before and after.
10 .S DUP .SIf you want to display just the top value of the stack and still have a copy of it left, you must duplicate it first. Remember the 'dot' consumes the value stored on the top of the stack.
DROP is a function which disposes the value on the top of the stack without displaying it. SWAP exchanges the top two items on the stack. OVER moves a copy of the second item on the stack to the top. This adds an item to the stack. ROT moves, not copies, the third item on the stack to the top and slides the original top two down to fill the space formally occupied by the item moved to the top.
Usually, these few functions will do almost everything you will want to do to the stack. But there are times you will want to do a few other things. You could, of course, define a function to suit your needs. Some of these are available. PICK requires the number of the item you wish to copy to the top of the stack and replaces the selection number with the value found at that location. ROLL also requires the number of the item you wish to move to the top of the stack and slides all of the rest of the values down. If you need to use these two functions very often you might want to rethink the solution to your problem. Othertimes it is easier to just go on and use them for a quick dirty solution.
MVP-FORTH is really a two stack machine. A second stack is usually implemented in FORTH. It behaves exactly as the system stack, but is not as efficient. It is called the return stack. The return stack is used by FORTH to keep track of the addresses to be executed next.
Only within a colon definition can you move values from the top of the return stack to the top of the data stack and back. There are occasions when this is particularly useful. It saves defining special variables for temporary use. But you must understand what you are doing and see that you finish the colon definition with the return stack in exactly the condition you found it.
The FORTH function which moves the top of the data stack to the top of the return stack is >R, ( 'to-R' ). The one to move the top of the return stack to the top of the data stack is named R>, ( 'R-from"). One of the advantages of using the top of the return stack for a temporary storage is that you can also copy the return stack back to the data stack with the FORTH function R@ ( 'R-fetch ).
The full exploration of the potential of these latter MVP-FORTH functions is probably best done by a study of the code using them. They are included here to call to your attention the fact that they exist.
One other data stack function which is convenient in conditional structures is ?DUP. This function does a DUP only on the condition that the value on the top of the stack is not 0. This saves a small amount of programming if you want to DROP the value if it is 0. This function is often used in conjunction with the conditional IF ... THEN structure to be discussed later.
Few exercises have been given in the use of these stack functions. You should make up your own exercises.
1 2 3 4 5 .SThis will place some numbers on the stack and show you what you have to start with. Then try each of the functions and reexamine the stack.
DUP .S . .S DUP .S DROP .S SWAP .S SWAP .S OVER .S DROP .S ROT .S ROT .S ROT .S ?DUP .S DROP .S 0 .S ?DUP .SIf you have trouble getting started, these will get you started. If at any time you are unsure of a function, you can interactively set up a similar exercise and test the function.
One of the problems with programs which do not run is that you have added some bug in the handling of the stack. There are several approaches to debugging such a program. One is to carefully adopt some format in which you can write out the stack contents after the execution of each FORTH function. In complicated programs this is probably the most efficient way to begin.
As you begin to think in FORTH, it will become almost second nature to use the stacks as it is to use pronouns in the English language. But even then, you will have occasional bugs which are difficult to find.
In such cases, you can scatter a number of .S functions through your definition. When the function is then executed each .S will display a copy of the stack. Occasionally, it is useful to echo the output on the printer so that you can carefully follow the progress.
The data stack is where FORTH temporarily stores values you or the program give the system. It saves the need of many temporary labels. It often clarifies the actual functioning of the program. The stack serves to pass parameters to successive functions. It is somewhat different from the manner of passing parameters in many programs. Where needed, it is possible to add more formal definition of parameters.