Forth Lesson 3
In the previous lesson we learned that:
- Forth words are described by stack diagrams like: ( argn .. arg0 -- resultm .. result0 )
- Comments are either ( this is a comment ) or \ the rest of the line is a comment
- Both "(" and "\" must be followed by whitespace
- Comments work by executing a word that then explicitly calls the input parser
Arithmetic and Logical Operators
We have already seen "+", which pops two numbers, adds them, and pushes the result. Here are some more numeric operators with their stack diagrams:
+ ( n1 n2 -- n1+n2 ) - ( n1 n2 -- n1-n2 ) * ( n1 n2 -- n1*n2 ) \ Signed multiplication / ( n1 n2 -- n1/n2 ) \ Signed division mod ( n1 n2 -- n1%n2 ) /mod ( n1 n2 -- n1%n2 n1/n2 ) and ( n1 n2 -- n1&n2 ) or ( n1 n2 -- n1|n2 ) xor ( n1 n2 -- n1^n2 ) lshift ( n1 n2 -- n1<<n2 ) << ( n1 n2 -- n1<<n2 ) \ Synonym for lshift rshift ( n1 n2 -- n1>>n2 ) \ Zero-extends >> ( n1 n2 -- n1>>n2 ) \ Synonym for rshift >>a ( n1 n2 -- n1>>n2 ) \ Sign-extends abs ( n -- u ) \ Absolute value max ( n1 n2 -- max ) \ Signed maximum min ( n1 n2 -- min ) \ Signed minimum umax ( u1 u2 -- max ) \ Unsigned maximum umin ( u1 u2 -- min ) \ Unsigned minimum invert ( n -- ~n ) \ Bitwise logical inversion negate ( n -- -n ) \ Arithmetic negation, i.e. 0-n 1- ( n -- n-1 ) \ Decrement by 1 1+ ( n -- n+1 ) \ Increment by 1 2/ ( n -- n/2 ) \ Divide by 2 2* ( n -- n*2 ) \ Multiply by 2
All of the above operators work on integers of the natural size for the machine (32 bits on 32-bit processors). There are some double-number arithmetic operators that let you treat pairs of stack items as extended numbers, but we won't worry about them for now.
Forth does not do data typing, so whether a number on the stack is signed or unsigned is purely a matter of how you wish to interpret it. You must choose the appropriate operator (e.g. umax vs max), instead of declaring the type of the data. When a stack diagram says "u1" instead of "n1", that is just a notational convention to remind you that the particular word interprets the value as unsigned; when the number is on the stack, it is just a group of bits that you can treat however you want by applying whatever operator you want.
Using Forth as a Calculator
Armed with those operators, plus the "." (pop and display) word that we have already seen, we can use Forth as an integer calculator. But since everything is stack based, we have to use Reverse Polish instead of algebraic syntax. Instead of writing down an algebraic expression with parentheses to control the grouping, we have to think about what to do first, and then do it.
So, suppose that we want to calculate ( 5 + ( 4 * 7 ) ). The multiplication has to be done first, before we can add the 5. We could perform that calculation in Forth with:
ok 4 7 * 5 + .
Breaking this down into the individual steps,
- push 4 on the stack
- push 7 on the stack
- pop two items from the stack, multiply them, and push the result (0x1c) back on the stack
- push 5 on the stack
- pop two items from the stack, add them, and push the result (0x21) back on the stack
- pop the stack and display it
The same calculation can be written in a different way
ok 5 4 7 * + .
In this version, we push all three numbers onto the stack at once, then perform the arithmetic - first the multiplication of 4 and 7, and finally the addition of 5 to the product.
Most Forth texts go to great lengths to explain how to do calculations in Reverse Polish, but I'm writing this for computer professionals and will assume that you either know this already or can figure it out. The key is that you have to think in terms of doing steps in order instead of writing a picture of the algebraic expression and letting the compiler sort it out.
Sometimes you need to copy or rearrange the stack to bring items to the correct position for further use. Four basic stack operators get the most use.
dup ( a -- a a ) drop ( a -- ) swap ( a b -- b a ) over ( a b -- a b a )
Stack operators will be examined in more detail in a later lesson.
Fractions and Floating Point Numbers
Some (perhaps most) Forth systems have a set of words to operate on floating point numbers. But floating point is not terribly useful for systems programming, so Open Firmware normally doesn't include floating point support (there is no reason it couldn't have it; it's just that it's rarely worth the trouble).
So if you need to do fractions, you will have to scale the integers. For example, if you need to calculate a fractional number to two decimal places, you could just premultiply the dividend by 100. There are various simple tricks like this that will let you avoid floating point for a wide variety of simple calculations.
The double-number words that I alluded to earlier have some fairly useful operators for scaling and unscaling without overflow, but I will leave that topic to a later lesson.
For now, just try out some calculations with the words you already know. Keep in mind that the default number base for Open Firmware is hexadecimal, so if you want do work in decimal you will first need to type "decimal" to switch the base.
Thus endeth the lesson