Sunday, June 14, 2009

Primary ColorForth Chapter 2

First some housekeeping:

Since Starting Forth is based on traditional Forth, there are some examples that won't work out of the box on ColorForth. Most of the required words can be easily implemented such as spaces back in chapter 1. Some words already exist but are not available in interactive mode because they have only been defined in the macro wordlist. I will define definitions needed for the Starting Forth examples as we go along. Alternatively here are the new definitions together.

/mod ab-qr /mod ;
mod ab-r mod ;
swap ab-ba swap ;
over ab-aba over ;
rot abc-bca push swap pop swap ;
2swap abcd-cdab push rot rot pop rot rot ;
2dup ab-abab over over ;
2over abcd-abcdab push push 2dup pop pop 2swap ;
2drop ab- drop drop ;

Forth Arithmetic -- Calculator Style

Starting Forth identifies the four fundamental arithmetic instructions in traditional Forth are +, -, *, and /. ColorForth has +, * and /. You might be wondering what happened to -. It is a macro that returns the one's complement (the same as invert in ANS Forth). This corresponds to the way - is used in Charles Moore's MISC processors since MuP21. You can still subtract by adding the negative. For constants just use the negative value. For variables or calculated results you can get the negative using NEGATE.

Starting Forth then gives several examples of postfix math. Note as mentioned in chapter 2, in ColorForth you don't need to use
. in interactive mode except as a shortcut for drop. You can also clear the entire stack using c. ColorForth continuously displays the parameter stack in the lower left of the screen. Try the following:

17 5 +
7 8 *
3 4 +
500 -300 +
5 6 *
20 4 /
c


An example of adding multiple numbers:

17 20 + 132 + 3 + 9 + .

Converting infix expressions to postfix (quizzie 2-a)

Here is a little trick I learned as a computer science undergraduate. Fully parenthesize your expression and move all operators to the end of the corresponding parenthesis. Examples:

c(a + b) => (c*(a+b)) => (c(a b)+)* => c a b + *

a*b/100 => ((a * b)/100) => ((a b)*100)/ => a b * 100 /

Try the following on your own:

(3a - b)/4 + c

(a + 1)/4

x(7x + 5)

Forth Arithmetic -- Definition Style

The next examples are for converting from yards or feet to inches. We know 1 yard = 36 inches and 1 foot = 12 inches. Here is the conversion code:

yards-inches 36 * ;
feet-inches 12 * ;

Usage example:

10 yards-inches
2 feet-inches

If we always want results to be in inches, we can define:

yards 36 * ;
feet 12 * ;
inches ;

to get the phrase

10 yards 2 feet + 9 inches +

Now perhaps you want to be able to use singular or plural words for better readability. Starting Forth used the following code:

: yard yards ;
: foot feet ;
: inch ;

That has the undesiable effect of adding an extra word call just for the sake of readability. ColorForth can do the same without the overhead using fall through factoring. The same definition can have multiple entry points. So in ColorForth you can define:

yard
yards 36 * ;
foot
feet 12 * ;
inch
inches ;

Note that yard and yards, foot and feet, inch and inches all point to the same memory location.

Now let's consider the earlier example of adding 5 numbers together.

17 20 + 132 + 3 + 9 +

The same thing could be accomplished by moving the
+'s to the end.

17 20 132 3 9 + + + +


Changing this to a definition we get:

5sum + + + + ;

Now say if in the same program sometimes we needed to add 5 numbers together and sometimes we needed to add 3? We could use fall through factoring here.

5sum + +
3sum + + ;

This time
5sum and 3sum point to different locations in memory. If you call 5sum it does the first 2 additions and then "falls through" to the remaining 2 additions in 3sum. If you call 3sum it just does the required 2 additions and returns.

An equation for another definition:

(a + b) * c

In postfix:

a b + c *

Alternatively we could say:

c * (a + b)

In postfix:

c a b + *

from which we can write the definition:

solution + * ;

It's a good time to point out that in Forth it's sometimes better to re-write an equation to make it easier to manipulate on the parameter stack. Here is the same code as a word problem:

If a jet plane flies at an average air speed of 600 mph and if it flies with a tail wind of 25 mph, how far will it travel in five hours?

If we define:

flight-dist + * ;

we could enter
5 600 25 flight-dist and get 3125 on the top of the stack.

Try it with different values, including head winds (negative values).


The Division Operators

The basic division in ColorForth (and traditional Forth as well) is /. This is an interger operator which discards the remainder.

22 5 /

This leaves 4 on the stack. There are two other division operators,
mod and /mod. By default they are in the macro wordset and only available during compilation. However they can be compiled to the Forth wordset.

mod ab-r mod ; returns the remainder
/mod ab-qr /mod ; returns quotient and remainder

Testing
/mod interactively: 22 4 /mod leaves 2 5 on the stack.

We can define a word quarters that takes a number of quarters and calculates the dollars and leftover quarters.

126 load q 0 quarters ones 0 ones
quarters 4 /mod . q .s . ones .s ;
go show blank text 22 quarters ;

Test
mod interactively: 22 4 mod leaves 2 on the stack.

Stack Manipulation

Swap

As the name suggests, this word takes the top too numbers on the stack and swaps them. Again this word is only in the macro wordlist. To use it interactively:

swap swap ;

Testing this interactively:
1 2 swap leaves 2 1 on the stack.

Dup

Dup takes the top number on the stack and duplicates it.
2 dup leaves 2 2 on the stack.

You can use dup to square a number.
4 dup * leaves 16 on the stack.

Over

Over is defined only in the macro wordlist. To use it interactively define:

over over ;

Over takes the second item on the stack and copies it to the top of the stack.

Testing it interactively,
1 2 over leaves 1 2 1 on the stack.

Thus the expression

a * (a + b)

can be written as:

over * +

Rot

Rot is in traditional Forth but is not by default defined in ColorForth. However defining it is simple. It requires a the words
push and pop. These words manipulate the second stack called the "return stack". Starting Forth doesn't cover this until another chapter. Basically push takes the top number from the parameter stack and moves it to the return stack. pop does the opposite. Using them rot is defined as:

rot push swap pop swap ;

Rot takes the 3rd item on the stack and rotates it to the top.

Testing it interactively:
1 2 3 rot leaves 2 3 1 on the stack.

Say if you wanted to evaluate the expression
ab - bc. First factor out the b's.

b*(a - c)

Now starting with
c b a on the stack we can use rot - *.

Drop

Drop, as its name suggests, drops the top item on the stack. Testing interactively:
1 2 drop leaves 1 on the stack.

Note: Look ma! No .s. Starting Forth mentions at this point that .s can be used to display the stack non destructively. But ColorForth shows the stack in interactive mode at the bottom left of the screen. Thus .s is not needed and not defined. Hence I can use .s is ColorForth for printing strings.

Stack Manipulation and Math Definitions (Quizzie 2-c)

1. Write a phrase which flips three items on the stack, leaving the middle number in the middle; that is:

a b c ==> c b a

Answer:
swap rot

2. Write a phrase that does what
over does, without using over.

Answer:
swap dup rot rot

3. Write a definition called
-rot, which rotates the top three stack items in the opposite direction from rot; that is:

a b c ==> c a b


Answer: -rot rot rot ;

Write definitions for the following:

4. (n+1) / n

Answer: 2c4 dup 1 + swap / ;

5. x(7x + 5)

Answer: 2c5 dup 7 * 5 + * ;

6.
9a2 - ba

a(9a - b)

Answer:
2c6 over 9 * swap - * ;

Playing Doubles

Many early Forths ran on 16 bit computers. As such "double" words, which allowed 32 bit math, were very important. ColorForth was initially written for a 32 bit computer and thus does not implement double words. However double stack manipulation words are not difficult to write.

2swap abcd-cdab push rot rot pop rot rot ;
2dup ab-abab over over ;
2over abcd-abcdab push push 2dup pop pop 2swap ;
2drop ab- drop drop ;

Problems -- Chapter 2

1. What's the difference between DUP DUP and 2DUP?

2. Write a phrase which will reverse the order of the top four items on the stack; that is,

( 1 2 3 4 -- 4 3 2 1 )

Answer:
reverse swap 2swap swap ;

3. Write a definition called 3DUP which will duplicate the top three numbers on the stack; for example,

Answer:
3dup abc-abcabc dup 2over rot ;

Write definitions for the following infix equations, given the stack effects shown:
4. a2 + ab + c ( c a b -- result )

a(a + b) + c

Answer:
prob4 over + * + ;

5. (a-b) / (a+b) ( a b -- result ) [answer]

Answer:
prob5 2dup - -rot + / ;

6. Write a set of words to compute prison sentences for hardened criminals such that the judge can enter:

CONVICTED-OF ARSON HOMICIDE TAX-EVASION ok
WILL-SERVE 35 years ok

or any series of crime beginning with the word CONVICTED-OF and ending with WILL-SERVE. Use these sentences

HOMICIDE 20 years
ARSON 10 years
BOOKMAKING 2 years
TAX-EVASION 5 years

Answer:

126 load years 0 years
convicted-of 0 ;
homicide 20 + ;
arson 10 + ;
bookmaking 2 + ;
tax-evasion 5 + ;
will-serve . years .s ;
ok show blank text convicted-of homicide arson tax-evasion will-serve ;

7. You're the inventory programmer at Maria's Egg Ranch. Define a word called EGG.CARTONS which expects on the stack the total number of eggs laid by the chickens today and prints out the number of cartons that can be filled with a dozen each, as well as the number of left-over eggs.

126 load cartons 0 cartons eggs 0 eggs
egg.cartons e-cr 12 /mod . cartons .s . eggs .s ;
ok show blank text 55 egg.cartons ;

Fall through factoring

The idea is that you can have multiple entry points to the same word. Here's an example. Say if you needed code for shifting right twice (4 *) and shifting right3 times (8 *). You could code that without factoring as:

4* 2* 2* ;
8* 2* 2* 2* ;

You could also code it using traditional factoring as:

4* 2* 2* ;
8* 2* 4* ;

Using fall through factoring that becomes:

8* 2*
4* 2* 2* ;

With fall through factoring 8* doesn't have the overhead of having to call 4*, but you still get the code density benefit of having 4* factored out. Fall through factoring can also be used to increase code readability without added overhead. Consider the example from Starting Forth chapter 2.

: YARDS 36 * ;
: FEET 12 * ;
: INCHES ;

: YARD YARDS ;
: FOOT FEET ;
: INCH INCHES ;

The only reason for yard, foot and inch is readability. Here is the same result using fall through factoring.

yard
yards 36 * ;
foot
feet 12 * ;
inch
inches ;

Note that there is now no overhead for yard, foot, or inch being their own separate words. Each word is a separate label for the same code address. In traditional Forth the words yard, foot and inch all have the overhead of making a call to their plural versions. Some traditional Forths have a word alias that can give a similar result as this last example, but that's not standard.

Labels:

Saturday, June 13, 2009

Typing ColorForth 4.1

Well I thought I had done my last post on "typing ColorForth" at version 4.0. That version does everything I want. However it doesn't quite do it the way I wanted it to. I took code I found on the web that types comment text to the screen, modified it to use yellow "immediate" mode for accessing variables, and then factored the word ".s" (stype in the original version) using inline macros. At least I thought I was using inline macros. The reason is that I wanted to make it clear what .s does. Again the code from the original stype.

stype 1 + dup @ dup 15 and select stype ;

Anyone who understands ColorForth's tail recursion feature can tell what
stype ; does. And select is explained in the shadow block comments. But what does 1 + dup @ dup 15 and do? Not obvious is it? 1 + dup @ dup increments then retrieves from the string address. 15 and gets the tag value. You can factor that out those two phrases as "nextw" and "tag", but that doesn't do anything for code density in this case because the words are only used once yet you still have the penalty (albeit small) of the extra call.

So I thought about trying to inline it using
cyan words. My thought was that I could define the words inside the macro wordlist and then use them as cyan words in the normal forth wordlist and everything would be inlined. I'd get the readability improvement from factoring without the overhead.

On retrospect I believe my understanding of cyan words was not right.
Cyan should be used inside macro wordlist and not the forth wordlist. I believe the result of using cyan for a word defined as a macro is that a call was inserted instead of inline code.

Now I'm not sure if this is right either. But there is a better way to get the results I want. One of the key features of ColorForth is fall through factoring. The idea is that you can have multiple entry points to the same word. Using fall through factoring I can rewrite
.s as this:

select jump rest done done done done done done done done norm cap caps done done done done ;
.s
nextw 1 + dup @ dup
tag f and select .s ;

I can now document nextw and tag in the corresponding shadow block. Again, the entire code.

+first 0 +rest 0
type space fffffff0 and unpack +first @ + emit
rest unpack if +rest @ + emit rest ; then drop drop ;
done drop drop pop drop ;
norm 0 +first ! 0 +rest ! type ;
cap 48 +first ! 0 +rest ! type ;
caps 48 +first ! 48 +rest ! type ;
select jump rest done done done done done done done done norm cap caps done done done done ;
.S
nextw 1 + dup @ dup
tag f and select .s ;

And the shadow block:

type w display one 32-bit word of text
rest w finish typing a word
done aw drop addr and word and return from stype
norm normal text
cap text starts with a capital
caps all caps
select wt handle word according to tag
.s a display contiguous white text
nextw a get next word and increment address
tag n get tag field from word. used by select.

The usage remains the same. "Hello World" in 2 lines of ColorForth.

126 load hello 0 Hello World, I speak ColorForth
ok show blank text hello .s ;

Note: This will probably be the last version of Typing ColorForth (at least I hope so). It does (almost*) everything I want it to do as is as efficient and readable as I can make it. To the critics (well meaning I'm sure) who don't understand why I was doing this, please look at the rest of the Primary ColorForth tutorial. Doing all of the examples in Starting Forth by looking up codes and "emitting" them would be an unecessary pain in the butt. And yes, I could just type in "Hello World" on a blank screen, but that would not be flexible enough for what I'm doing.

(* There is one other thing I'd like to do. The current version sticks in an extra space before typing anything. Sometimes this throws the formatting off. But I'm going to quibble about that...for now.)

Labels: