Forth Lesson 3

From OLPC
Revision as of 17:33, 6 January 2007 by Xavi (talk | contribs) (categorization)
Jump to navigation Jump to search

Review

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 )
 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

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,

  1. push 4 on the stack
  2. push 7 on the stack
  3. pop two items from the stack, multiply them, and push the result (0x1c) back on the stack
  4. push 5 on the stack
  5. pop two items from the stack, add them, and push the result (0x21) back on the stack
  6. 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.

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

Next Lesson