Wednesday, June 3, 2009

Primary ColorForth Chapter 1

As mentioned in the intro, Primary ColorForth is loosely based on Starting Forth. You can follow along in it for this tutorial series either with a hardcopy version or online.

Starting Forth chapter 11 starts by describing a way of organizing work by defining useful tasks and giving them a name, then grouping tasks together into larger tasks and naming them.

It gives the example of an embedded washing machine program which we will explore.


Washing Machine Example

Here is the top level washing machine program in ColorForth.

washer wash spin rinse spin ;

In ColorForth red words signify the start of a new definition. So
washer is the new procedure. The ";" represents the call return. Green words are ones that have already been defined and are compiled into the new definition. Note that unlike traditional Forth words cannot be defined from the command line. All words must be defined in the editor. Then the block containing the source text must be loaded. For example, to define new words in block 22 you would use 22 edit, then to compile then you would use 22 load.

Let's look at the definition of
rinse.

rinse fill agitate drain ;

And now the definition of
fill.

fill faucets open till-full faucets close ;

The definition includes "things" (faucets) and verbs (open and close). The word "till-full" represents a delay loop. After the faucets "open" the word waits "till-full" before they "close".

Every word must be defined before it can be compiled into a definition, so
fill must be compiled before rinse and rinse must be compiled before washer.

fill faucets open till-full faucets close ;
rinse fill agitate drain ;
washer wash spin rinse spin ;

This system is just an example. For a real system the definitions for open, close, faucets, till-full, agitate, drain and spin would all have to be defined. At some point it would be direct machine code to activate stepper motors, read sensors etc.

Putting On a Show

Forth was originally designed around a TTY display model. But ColorForth uses a GUI display, albeit one that is MUCH simpler than MS Windows, X-Windows or MacIntosh. So getting things displayed on the screen is a bit tricker than traditional Forth, but simpler than raw programming of most GUI operating system. The ColorForth word
show creates a display task using the words following it.

Consider this definition.

ok show text 93 emit keyboard ;

This creates a new definition
ok that draws a "*" at the top left corner of the screen. The word text moves the cursor to the top left of the screen and sets the drawing color to white. The word emit takes a number off the stack and draws the corresponding charecter. It's the same as emit in traditional Forth. Note that the character set is different from ASCII. You can look up the characters in a code map, or you can use the command icons. The word keyboard redraws the keyboard and stack.

Chapter 1 of Starting Forth builds up an example on how to draw the letter "F" as a large character using "ASCII art". Here is a walkthrough of that example using colorForth.

First we need to define the word
spaces which is built into traditional Forth.

spaces for space next ;

Note ColorForth uses
for...next for its counted loop structure. Using spaces with what we learned earlier we can code the following:

spaces for space next ;
ok show text 15 spaces 93 emit ;

Notice when we run this, the word
spaces doesn't erase the text already on the screen, but just moves the star over. That's another example of how graphical CF is different from TTY TF.

Now let's define
star as it's own word.

spaces for space next ;
star 93 emit ;
go show text 15 spaces star ;

Moving sections of code into their own definitions is called factoring. Factoring is used to increase readability and/or code density.

The next words define the "F" using combinations of the above defined words. Note that "cr" stands for "carriage return". That simply moves the cursor to the next line.

side star spaces star ;
margin cr 30 spaces ;
blip margin star ;
bar margin 5 stars ;
f bar blip bar blip blip cr ;
ok show text f keyboard ;

After loading the block containing these words and typing
ok from the command line a large "F" will be displayed on the screen.

Hello World from ColorForth

The next Starting Forth example is a variation on the venerable "Hello World" program. This particular program has cause some needless controvery and confusion over the years. Sure you
emit each character one at a time, but that's not the most readable solution nor the most flexible. ColorForth can type blocks of text just like traditional Forth. And since ColorForth is extensible, just like traditional Forth is extensible, you extend it to where you can do a program like "Hello World" in just two lines.

First you have to understand how text is stored in the ColorForth editor. ColorForth doesn't store ASCII characters in bytes. Instead it packs several characters in one 32 bit word using Shannon encoding, eabling it to store 5 to 7 characters plus a color token in one word. ColorForth includes a word
unpack for decompressing such words. We have already discussed red and green words. ColorForth uses white words for comments. Comments can be as all lowercase, all upper case, or mixed case. So one way to display a block of text is to store it as comments in a Forth block, put that address on the stack, and then unpack and properly display the words until a non comment word is reached. Here is the code to set this up:

+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 @
tag dup 15 and select .s ;

That may look like a lot, but realize that you can load this block whenever you need it or you can even have it load automatically when ColorForth boots. Here is an example of its usage (assuming the code was stored in block 126).

126 load s 0 Hello world, I speak ColorForth
ok show text s .s ;

Note that magenta words like
s define variables. Variables in ColorForth point to the blocks where they are defined as opposed to the dictionary. So s is being used here to point to the comment block of text we want to type.

Run-time Versus Compile-time Versus Edit Time

Starting Forth then goes into a discussion about "run time versus compile time". This is a good time (no pun intended) to go into the other color states in ColorForth. We've already gone over how
red defines a new word and how green compiles a previously defined word into a new definition. Green words are executed at run time. There are also yellow words that are executed at compile time. Consider the "hello world" example given earlier.

126 load s 0 Hello world, I speak ColorForth
ok show text s .s ;

Note the phrase
126 load. This will load the text display code in block 126 as soon as the current block is loaded. You don't have to wait until ok is run. Also note that the acess to the variable s is in yellow too. A variable is really just a name for an address. Why wait until run time to look up the address? Why not look it up at compile time and compile the address directly into the code? Accessing variables as yellow words instead of green ones accomplishes that. In fact that is the preferred way.

Another way to use yellow words are with constants. When a constant is coded in yellow its value is compiled into the code on the yellow to green transition. This is also true for calculations that are done in yellow. Using the earlier "big F" exampe:

side star spaces star ;
margin cr 30 spaces ;
blip margin star ;
bar margin 5 stars ;
f bar blip bar blip blip cr ;
ok show text f keyboard ;

Yellow words can also be used to do calculations that can be determined at compile time such as calculations based on a constant.

Cyan words are used to create inline macros.

The Dictionary

ColorForth, like traditional Forth, stores definitions in a dictionary. One difference is the way ColorForth handles numbers. In traditional Forth the interpreter first looks in the dictionary find a word and if it's not found it checks to see if it is a number. The ColorForth editor handles numbers differently. So the compiler never has to check to see if something is really a number.

The Stack: Forth's Worksite for Arithmetic

If you are already familiar with Forth (or with HP scientific calculators) you know you to use the stack. If not here's a brief overview. Everytime a number is entered it is pushed on the stack. Forth arithmetic words will take numbers off the stack, do some operation with them, and return the results to the stack. To do 3 + 4 you must first push the 3 and the 4 to the stack then execute the "+" word.

3 4 +

Note that the current state of the stack is shown on the bottom left of the screen. If you use the dot command
. by itself all you will notice is that one number drops off. In traditional Forth . displays the top number on the stack. If you use . inside of a show word you get the traditional result.

go show 3 4 + . ;

Problems

Problem 1-1: Define a word called gift that, when executed, will type out the name of some .gift, a word .giver that prints out a person's name, and a word .thanks that prints out something like this:

Dear Stephanie, Thanks for the bookends.

Solution:

gift 0 bookends. giver 0 Stephanie, dear 0 Dear thank 0 Thanks for the
.gift gift .s ;
.giver giver .s ;
.thank thank .s ;
.dear dear .s ;
thanks show black screen text .dear .giver .thank .gift ;

Alternatively:

gift 0 bookends. giver 0 Stephanie, dear 0 Dear thank 0 Thanks for the
thanks show black screen text dear .s giver .s thank .s gift .s ;

Problem 1-2: Define a word
tenless that takes a number on the stack, substracts 10, and returns the answer on the stack. (Hint: You can use +.) Be sure to include stack effects.

Solution

tenless n-n' -10 + ;

Problem 1-3: After entering the words in Problems 1-1, enter a new definition for
.giver to print someone else's name, then without redefining thanks, execute thanks again. Can you explain why thanks still prints out the first giver's name?

Solution

gift 0 bookends. giver 0 Stephanie, dear 0 Dear thank 0 Thanks for the
.gift gift .s ;
.giver giver .s ;
.thank thank .s ;
.dear dear .s ;
thanks .dear .giver .thank .gift ; giver 0 Jill
.giver giver .s ;
go show black screen text thanks .giver ;

Alternatively:

gift 0 bookends. giver 0 Stephanie, dear 0 Dear thank 0 Thanks for the
thanks dear .s giver .s thank .s .gift .s ; giver 0 Jill
go show black screen text thanks giver .s ;


Whenever a new word is compiled it is appended to the end of the dictionary. The old definition isn't overwritten, but the interpreter starts at the newest definition and searches backwards so it doesn't find the older definition. However words that were defined using the old definition still work the same.

5 comments:

Pavel Klinkovsky said...

Dear John, can I have a question? How many characters are preallocated in the font table? I would like to add my own characters (icons), but I would not like to overwrite a code somewhere...

Thanks in adnvance.

Pavel

johnmdrake said...

I have no idea. Everything past 127 looks pretty random to me. In fact some of the stuff before 127 (between 96 and 127) looks random.

I don't think making changes to fonts is a problem. After all, they're just characters. Your system isn't going to quit working if you overwrite a character that's already being used somewhere. The worst that could happen is that something else might display differently.

Pavel Klinkovsky said...

I had found somewhere there is 6 KB of font table. It means there is a space for 128 characters...

Pavel

johnmdrake said...

That makes sense. If you remember where you saw that let me know so I can add the reference into the documentation effort.

Anonymous said...

John -- thank you so much! This is incredibly useful. It has been frustrating trying to understand and use colorForth, but it is slowly coming together. Persistence does pay off I guess.

Please keep up the great work. You're an absolute inspiration with your dogged determination.

Chuck and his team is slowly (glacier-esque) coming to the realization that incremental bridges and improvements will allow more people to more fully embrace his Forth methods. People like you who are filled with burning curiosity are the human bridges that socialize and implement these incremental changes to enable others to make the jump to "10x better". I'd be happy with 1+ better.